]> git.sesse.net Git - casparcg/blob - modules/screen/consumer/screen_consumer.cpp
Fixed compilation problem in Linux
[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 #include <common/future.h>
36 #include <common/timer.h>
37 #include <common/param.h>
38
39 //#include <windows.h>
40
41 #include <ffmpeg/producer/filter/filter.h>
42
43 #include <core/video_format.h>
44 #include <core/frame/frame.h>
45 #include <core/consumer/frame_consumer.h>
46 #include <core/interaction/interaction_sink.h>
47 #include <core/help/help_sink.h>
48 #include <core/help/help_repository.h>
49
50 #include <boost/circular_buffer.hpp>
51 #include <boost/lexical_cast.hpp>
52 #include <boost/property_tree/ptree.hpp>
53 #include <boost/thread.hpp>
54 #include <boost/algorithm/string.hpp>
55
56 #include <tbb/atomic.h>
57 #include <tbb/concurrent_queue.h>
58 #include <tbb/parallel_for.h>
59
60 #include <asmlib.h>
61
62 #include <algorithm>
63 #include <vector>
64
65 #if defined(_MSC_VER)
66 #pragma warning (push)
67 #pragma warning (disable : 4244)
68 #endif
69 extern "C" 
70 {
71         #define __STDC_CONSTANT_MACROS
72         #define __STDC_LIMIT_MACROS
73         #include <libavcodec/avcodec.h>
74         #include <libavutil/imgutils.h>
75 }
76 #if defined(_MSC_VER)
77 #pragma warning (pop)
78 #endif
79
80 namespace caspar { namespace screen {
81                 
82 enum class stretch
83 {
84         none,
85         uniform,
86         fill,
87         uniform_to_fill
88 };
89
90 struct configuration
91 {
92         enum class aspect_ratio
93         {
94                 aspect_4_3 = 0,
95                 aspect_16_9,
96                 aspect_invalid,
97         };
98                 
99         std::wstring    name                            = L"ogl";
100         int                             screen_index            = 0;
101         screen::stretch stretch                         = screen::stretch::fill;
102         bool                    windowed                        = true;
103         bool                    auto_deinterlace        = true;
104         bool                    key_only                        = false;
105         aspect_ratio    aspect                          = aspect_ratio::aspect_invalid;
106         bool                    vsync                           = true;
107         bool                    interactive                     = true;
108 };
109
110 struct screen_consumer : boost::noncopyable
111 {
112         const configuration                                                                     config_;
113         core::video_format_desc                                                         format_desc_;
114         int                                                                                                     channel_index_;
115
116         GLuint                                                                                          texture_                = 0;
117         std::vector<GLuint>                                                                     pbos_                   = std::vector<GLuint> { 0, 0 };
118                         
119         float                                                                                           width_;
120         float                                                                                           height_;
121         int                                                                                                     screen_x_;
122         int                                                                                                     screen_y_;
123         int                                                                                                     screen_width_   = format_desc_.width;
124         int                                                                                                     screen_height_  = format_desc_.height;
125         int                                                                                                     square_width_   = format_desc_.square_width;
126         int                                                                                                     square_height_  = format_desc_.square_height;
127
128         sf::Window                                                                                      window_;
129
130         spl::shared_ptr<diagnostics::graph>                                     graph_;
131         caspar::timer                                                                           perf_timer_;
132         caspar::timer                                                                           tick_timer_;
133
134         caspar::prec_timer                                                                      wait_timer_;
135
136         tbb::concurrent_bounded_queue<core::const_frame>        frame_buffer_;
137         core::interaction_sink*                                                         sink_;
138
139         boost::thread                                                                           thread_;
140         tbb::atomic<bool>                                                                       is_running_;
141         tbb::atomic<int64_t>                                                            current_presentation_age_;
142
143         ffmpeg::filter                                                                          filter_;
144 public:
145         screen_consumer(
146                         const configuration& config,
147                         const core::video_format_desc& format_desc,
148                         int channel_index,
149                         core::interaction_sink* sink) 
150                 : config_(config)
151                 , format_desc_(format_desc)
152                 , channel_index_(channel_index)
153                 , sink_(sink)
154                 , filter_([&]() -> ffmpeg::filter
155                 {                       
156                         const auto sample_aspect_ratio = 
157                                 boost::rational<int>(
158                                         format_desc.square_width, 
159                                         format_desc.square_height) /
160                                 boost::rational<int>(
161                                         format_desc.width, 
162                                         format_desc.height);
163
164                         return ffmpeg::filter(
165                                 format_desc.width,
166                                 format_desc.height,
167                                 boost::rational<int>(format_desc.duration, format_desc.time_scale),
168                                 boost::rational<int>(format_desc.time_scale, format_desc.duration),
169                                 sample_aspect_ratio,
170                                 AV_PIX_FMT_BGRA,
171                                 { AV_PIX_FMT_BGRA },
172                                 format_desc.field_mode == core::field_mode::progressive || !config.auto_deinterlace ? "" : "YADIF=1:-1");
173                 }())
174         {               
175                 if (format_desc_.format == core::video_format::ntsc && config_.aspect == configuration::aspect_ratio::aspect_4_3)
176                 {
177                         // Use default values which are 4:3.
178                 }
179                 else
180                 {
181                         if (config_.aspect == configuration::aspect_ratio::aspect_16_9)
182                                 square_width_ = (format_desc.height*16)/9;
183                         else if (config_.aspect == configuration::aspect_ratio::aspect_4_3)
184                                 square_width_ = (format_desc.height*4)/3;
185                 }
186
187                 frame_buffer_.set_capacity(1);
188                 
189                 graph_->set_color("tick-time", diagnostics::color(0.0f, 0.6f, 0.9f));   
190                 graph_->set_color("frame-time", diagnostics::color(0.1f, 1.0f, 0.1f));
191                 graph_->set_color("dropped-frame", diagnostics::color(0.3f, 0.6f, 0.3f));
192                 graph_->set_text(print());
193                 diagnostics::register_graph(graph_);
194                                                                         
195                 /*DISPLAY_DEVICE d_device = {sizeof(d_device), 0};
196                 std::vector<DISPLAY_DEVICE> displayDevices;
197                 for(int n = 0; EnumDisplayDevices(NULL, n, &d_device, NULL); ++n)
198                         displayDevices.push_back(d_device);
199
200                 if(config_.screen_index >= displayDevices.size())
201                         CASPAR_LOG(warning) << print() << L" Invalid screen-index: " << config_.screen_index;
202                 
203                 DEVMODE devmode = {};
204                 if(!EnumDisplaySettings(displayDevices[config_.screen_index].DeviceName, ENUM_CURRENT_SETTINGS, &devmode))
205                         CASPAR_LOG(warning) << print() << L" Could not find display settings for screen-index: " << config_.screen_index;
206                 
207                 screen_x_               = devmode.dmPosition.x;
208                 screen_y_               = devmode.dmPosition.y;
209                 screen_width_   = config_.windowed ? square_width_ : devmode.dmPelsWidth;
210                 screen_height_  = config_.windowed ? square_height_ : devmode.dmPelsHeight;*/
211                 screen_x_               = 0;
212                 screen_y_               = 0;
213                 screen_width_   = square_width_;
214                 screen_height_  = square_height_;
215                 
216                 is_running_ = true;
217                 current_presentation_age_ = 0;
218                 thread_ = boost::thread([this]{run();});
219         }
220         
221         ~screen_consumer()
222         {
223                 is_running_ = false;
224                 frame_buffer_.try_push(core::const_frame::empty());
225                 thread_.join();
226         }
227
228         void init()
229         {
230                 window_.create(sf::VideoMode(screen_width_, screen_height_, 32), u8(L"Screen consumer " + channel_and_format()), config_.windowed ? sf::Style::Resize | sf::Style::Close : sf::Style::Fullscreen);
231                 window_.setMouseCursorVisible(config_.interactive);
232                 window_.setPosition(sf::Vector2i(screen_x_, screen_y_));
233                 window_.setSize(sf::Vector2u(screen_width_, screen_height_));
234                 window_.setActive();
235                 
236                 if(!GLEW_VERSION_2_1 && glewInit() != GLEW_OK)
237                         CASPAR_THROW_EXCEPTION(gl::ogl_exception() << msg_info("Failed to initialize GLEW."));
238
239                 if(!GLEW_VERSION_2_1)
240                         CASPAR_THROW_EXCEPTION(not_supported() << msg_info("Missing OpenGL 2.1 support."));
241
242                 GL(glEnable(GL_TEXTURE_2D));
243                 GL(glDisable(GL_DEPTH_TEST));           
244                 GL(glClearColor(0.0, 0.0, 0.0, 0.0));
245                 GL(glViewport(0, 0, format_desc_.width, format_desc_.height));
246                 GL(glLoadIdentity());
247                                 
248                 calculate_aspect();
249                         
250                 GL(glGenTextures(1, &texture_));
251                 GL(glBindTexture(GL_TEXTURE_2D, texture_));
252                 GL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR));
253                 GL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR));
254                 GL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP));
255                 GL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP));
256                 GL(glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, format_desc_.width, format_desc_.height, 0, GL_BGRA, GL_UNSIGNED_BYTE, 0));
257                 GL(glBindTexture(GL_TEXTURE_2D, 0));
258                                         
259                 GL(glGenBuffers(2, pbos_.data()));
260                         
261                 glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, pbos_[0]);
262                 glBufferDataARB(GL_PIXEL_UNPACK_BUFFER_ARB, format_desc_.size, 0, GL_STREAM_DRAW_ARB);
263                 glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, pbos_[1]);
264                 glBufferDataARB(GL_PIXEL_UNPACK_BUFFER_ARB, format_desc_.size, 0, GL_STREAM_DRAW_ARB);
265                 glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0);
266                 
267                 window_.setVerticalSyncEnabled(config_.vsync);
268
269                 if (config_.vsync)
270                 {
271                         CASPAR_LOG(info) << print() << " Enabled vsync.";
272                 }
273                 /*auto wglSwapIntervalEXT = reinterpret_cast<void(APIENTRY*)(int)>(wglGetProcAddress("wglSwapIntervalEXT"));
274                 if(wglSwapIntervalEXT)
275                 {
276                         if(config_.vsync)
277                         {
278                                 wglSwapIntervalEXT(1);
279                                 CASPAR_LOG(info) << print() << " Enabled vsync.";
280                         }
281                         else
282                                 wglSwapIntervalEXT(0);
283                 }*/
284
285                 CASPAR_LOG(info) << print() << " Successfully Initialized.";
286         }
287
288         void uninit()
289         {               
290                 if(texture_)
291                         glDeleteTextures(1, &texture_);
292
293                 for (auto& pbo : pbos_)
294                 {
295                         if(pbo)
296                                 glDeleteBuffers(1, &pbo);
297                 }
298         }
299
300         void run()
301         {
302                 try
303                 {
304                         init();
305
306                         while(is_running_)
307                         {                       
308                                 try
309                                 {
310                                         sf::Event e;            
311                                         while(window_.pollEvent(e))
312                                         {
313                                                 if (e.type == sf::Event::Resized)
314                                                         calculate_aspect();
315                                                 else if (e.type == sf::Event::Closed)
316                                                         is_running_ = false;
317                                                 else if (config_.interactive && sink_)
318                                                 {
319                                                         switch (e.type)
320                                                         {
321                                                         case sf::Event::MouseMoved:
322                                                                 {
323                                                                         auto& mouse_move = e.mouseMove;
324                                                                         sink_->on_interaction(spl::make_shared<core::mouse_move_event>(
325                                                                                         1,
326                                                                                         static_cast<double>(mouse_move.x) / screen_width_,
327                                                                                         static_cast<double>(mouse_move.y) / screen_height_));
328                                                                 }
329                                                                 break;
330                                                         case sf::Event::MouseButtonPressed:
331                                                         case sf::Event::MouseButtonReleased:
332                                                                 {
333                                                                         auto& mouse_button = e.mouseButton;
334                                                                         sink_->on_interaction(spl::make_shared<core::mouse_button_event>(
335                                                                                         1,
336                                                                                         static_cast<double>(mouse_button.x) / screen_width_,
337                                                                                         static_cast<double>(mouse_button.y) / screen_height_,
338                                                                                         static_cast<int>(mouse_button.button),
339                                                                                         e.type == sf::Event::MouseButtonPressed));
340                                                                 }
341                                                                 break;
342                                                         case sf::Event::MouseWheelMoved:
343                                                                 {
344                                                                         auto& wheel_moved = e.mouseWheel;
345                                                                         sink_->on_interaction(spl::make_shared<core::mouse_wheel_event>(
346                                                                                         1,
347                                                                                         static_cast<double>(wheel_moved.x) / screen_width_,
348                                                                                         static_cast<double>(wheel_moved.y) / screen_height_,
349                                                                                         wheel_moved.delta));
350                                                                 }
351                                                                 break;
352                                                         }
353                                                 }
354                                         }
355                         
356                                         auto frame = core::const_frame::empty();
357                                         frame_buffer_.pop(frame);
358
359                                         render_and_draw_frame(frame);
360                                         
361                                         /*perf_timer_.restart();
362                                         render(frame);
363                                         graph_->set_value("frame-time", perf_timer_.elapsed()*format_desc_.fps*0.5);    
364
365                                         window_.Display();*/
366
367                                         current_presentation_age_ = frame.get_age_millis();
368                                         graph_->set_value("tick-time", tick_timer_.elapsed()*format_desc_.fps*0.5);     
369                                         tick_timer_.restart();
370                                 }
371                                 catch(...)
372                                 {
373                                         CASPAR_LOG_CURRENT_EXCEPTION();
374                                         is_running_ = false;
375                                 }
376                         }
377
378                         uninit();
379                 }
380                 catch(...)
381                 {
382                         CASPAR_LOG_CURRENT_EXCEPTION();
383                 }
384         }
385
386         void try_sleep_almost_until_vblank()
387         {
388                 static const double THRESHOLD = 0.003;
389                 double threshold = config_.vsync ? THRESHOLD : 0.0;
390
391                 auto frame_time = 1.0 / (format_desc_.fps * format_desc_.field_count);
392
393                 wait_timer_.tick(frame_time - threshold);
394         }
395
396         void wait_for_vblank_and_display()
397         {
398                 try_sleep_almost_until_vblank();
399                 window_.display();
400                 // Make sure that the next tick measures the duration from this point in time.
401                 wait_timer_.tick(0.0);
402         }
403
404         spl::shared_ptr<AVFrame> get_av_frame()
405         {               
406                 spl::shared_ptr<AVFrame> av_frame(avcodec_alloc_frame(), av_free);      
407                 avcodec_get_frame_defaults(av_frame.get());
408                                                 
409                 av_frame->linesize[0]           = format_desc_.width*4;                 
410                 av_frame->format                        = PIX_FMT_BGRA;
411                 av_frame->width                         = format_desc_.width;
412                 av_frame->height                        = format_desc_.height;
413                 av_frame->interlaced_frame      = format_desc_.field_mode != core::field_mode::progressive;
414                 av_frame->top_field_first       = format_desc_.field_mode == core::field_mode::upper ? 1 : 0;
415
416                 return av_frame;
417         }
418
419         void render_and_draw_frame(core::const_frame frame)
420         {
421                 if(static_cast<size_t>(frame.image_data().size()) != format_desc_.size)
422                         return;
423
424                 if(screen_width_ == 0 && screen_height_ == 0)
425                         return;
426                                         
427                 perf_timer_.restart();
428                 auto av_frame = get_av_frame();
429                 av_frame->data[0] = const_cast<uint8_t*>(frame.image_data().begin());
430
431                 filter_.push(av_frame);
432                 auto frames = filter_.poll_all();
433
434                 if (frames.empty())
435                         return;
436
437                 if (frames.size() == 1)
438                 {
439                         render(frames[0]);
440                         graph_->set_value("frame-time", perf_timer_.elapsed() * format_desc_.fps * 0.5);
441
442                         wait_for_vblank_and_display(); // progressive frame
443                 }
444                 else if (frames.size() == 2)
445                 {
446                         render(frames[0]);
447                         double perf_elapsed = perf_timer_.elapsed();
448
449                         wait_for_vblank_and_display(); // field1
450
451                         perf_timer_.restart();
452                         render(frames[1]);
453                         perf_elapsed += perf_timer_.elapsed();
454                         graph_->set_value("frame-time", perf_elapsed * format_desc_.fps * 0.5);
455
456                         wait_for_vblank_and_display(); // field2
457                 }
458         }
459
460         void render(spl::shared_ptr<AVFrame> av_frame)
461         {
462                 GL(glBindTexture(GL_TEXTURE_2D, texture_));
463
464                 GL(glBindBuffer(GL_PIXEL_UNPACK_BUFFER, pbos_[0]));
465                 GL(glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, format_desc_.width, format_desc_.height, GL_BGRA, GL_UNSIGNED_BYTE, 0));
466
467                 GL(glBindBuffer(GL_PIXEL_UNPACK_BUFFER, pbos_[1]));
468                 GL(glBufferData(GL_PIXEL_UNPACK_BUFFER, format_desc_.size, 0, GL_STREAM_DRAW));
469
470                 auto ptr = reinterpret_cast<char*>(GL2(glMapBuffer(GL_PIXEL_UNPACK_BUFFER, GL_WRITE_ONLY)));
471                 if(ptr)
472                 {
473                         if(config_.key_only)
474                         {
475                                 tbb::parallel_for(tbb::blocked_range<int>(0, format_desc_.height), [&](const tbb::blocked_range<int>& r)
476                                 {
477                                         for(int n = r.begin(); n != r.end(); ++n)
478                                                 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);
479                                 });
480                         }
481                         else
482                         {       
483                                 tbb::parallel_for(tbb::blocked_range<int>(0, format_desc_.height), [&](const tbb::blocked_range<int>& r)
484                                 {
485                                         for(int n = r.begin(); n != r.end(); ++n)
486                                                 A_memcpy(ptr+n*format_desc_.width*4, av_frame->data[0]+n*av_frame->linesize[0], format_desc_.width*4);
487                                 });
488                         }
489                         
490                         GL(glUnmapBuffer(GL_PIXEL_UNPACK_BUFFER)); // release the mapped buffer
491                 }
492
493                 GL(glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0));
494                                 
495                 GL(glClear(GL_COLOR_BUFFER_BIT));                       
496                 glBegin(GL_QUADS);
497                                 glTexCoord2f(0.0f,        1.0f);        glVertex2f(-width_, -height_);
498                                 glTexCoord2f(1.0f,        1.0f);        glVertex2f( width_, -height_);
499                                 glTexCoord2f(1.0f,        0.0f);        glVertex2f( width_,  height_);
500                                 glTexCoord2f(0.0f,        0.0f);        glVertex2f(-width_,  height_);
501                 glEnd();
502                 
503                 GL(glBindTexture(GL_TEXTURE_2D, 0));
504
505                 std::rotate(pbos_.begin(), pbos_.begin() + 1, pbos_.end());
506         }
507
508
509         std::future<bool> send(core::const_frame frame)
510         {
511                 if(!frame_buffer_.try_push(frame))
512                         graph_->set_tag("dropped-frame");
513
514                 return make_ready_future(is_running_.load());
515         }
516
517         std::wstring channel_and_format() const
518         {
519                 return L"[" + boost::lexical_cast<std::wstring>(channel_index_) + L"|" + format_desc_.name + L"]";
520         }
521
522         std::wstring print() const
523         {       
524                 return config_.name + channel_and_format();
525         }
526         
527         void calculate_aspect()
528         {
529                 if(config_.windowed)
530                 {
531                         screen_height_ = window_.getSize().y;
532                         screen_width_ = window_.getSize().x;
533                 }
534                 
535                 GL(glViewport(0, 0, screen_width_, screen_height_));
536
537                 std::pair<float, float> target_ratio = None();
538                 if (config_.stretch == screen::stretch::fill)
539                         target_ratio = Fill();
540                 else if (config_.stretch == screen::stretch::uniform)
541                         target_ratio = Uniform();
542                 else if (config_.stretch == screen::stretch::uniform_to_fill)
543                         target_ratio = UniformToFill();
544
545                 width_ = target_ratio.first;
546                 height_ = target_ratio.second;
547         }
548                 
549         std::pair<float, float> None()
550         {
551                 float width = static_cast<float>(square_width_)/static_cast<float>(screen_width_);
552                 float height = static_cast<float>(square_height_)/static_cast<float>(screen_height_);
553
554                 return std::make_pair(width, height);
555         }
556
557         std::pair<float, float> Uniform()
558         {
559                 float aspect = static_cast<float>(square_width_)/static_cast<float>(square_height_);
560                 float width = std::min(1.0f, static_cast<float>(screen_height_)*aspect/static_cast<float>(screen_width_));
561                 float height = static_cast<float>(screen_width_*width)/static_cast<float>(screen_height_*aspect);
562
563                 return std::make_pair(width, height);
564         }
565
566         std::pair<float, float> Fill()
567         {
568                 return std::make_pair(1.0f, 1.0f);
569         }
570
571         std::pair<float, float> UniformToFill()
572         {
573                 float wr = static_cast<float>(square_width_)/static_cast<float>(screen_width_);
574                 float hr = static_cast<float>(square_height_)/static_cast<float>(screen_height_);
575                 float r_inv = 1.0f/std::min(wr, hr);
576
577                 float width = wr*r_inv;
578                 float height = hr*r_inv;
579
580                 return std::make_pair(width, height);
581         }
582 };
583
584
585 struct screen_consumer_proxy : public core::frame_consumer
586 {
587         core::monitor::subject                          monitor_subject_;
588         const configuration                                     config_;
589         std::unique_ptr<screen_consumer>        consumer_;
590         core::interaction_sink*                         sink_;
591
592 public:
593
594         screen_consumer_proxy(const configuration& config, core::interaction_sink* sink)
595                 : config_(config)
596                 , sink_(sink)
597         {
598         }
599         
600         // frame_consumer
601
602         void initialize(const core::video_format_desc& format_desc, int channel_index) override
603         {
604                 consumer_.reset();
605                 consumer_.reset(new screen_consumer(config_, format_desc, channel_index, sink_));
606         }
607
608         int64_t presentation_frame_age_millis() const override
609         {
610                 return consumer_ ? static_cast<int64_t>(consumer_->current_presentation_age_) : 0;
611         }
612
613         std::future<bool> send(core::const_frame frame) override
614         {
615                 return consumer_->send(frame);
616         }
617         
618         std::wstring print() const override
619         {
620                 return consumer_ ? consumer_->print() : L"[screen_consumer]";
621         }
622
623         std::wstring name() const override
624         {
625                 return L"screen";
626         }
627
628         boost::property_tree::wptree info() const override
629         {
630                 boost::property_tree::wptree info;
631                 info.add(L"type", L"screen");
632                 info.add(L"key-only", config_.key_only);
633                 info.add(L"windowed", config_.windowed);
634                 info.add(L"auto-deinterlace", config_.auto_deinterlace);
635                 info.add(L"vsync", config_.vsync);
636                 return info;
637         }
638
639         bool has_synchronization_clock() const override
640         {
641                 return false;
642         }
643         
644         int buffer_depth() const override
645         {
646                 return 1;
647         }
648
649         int index() const override
650         {
651                 return 600 + (config_.key_only ? 10 : 0) + config_.screen_index;
652         }
653
654         core::monitor::subject& monitor_output()
655         {
656                 return monitor_subject_;
657         }
658 };      
659
660 void describe_consumer(core::help_sink& sink, const core::help_repository& repo)
661 {
662         sink.short_description(L"Displays the contents of a channel on screen using OpenGL.");
663         sink.syntax(
664                         L"SCREEN "
665                         L"{[screen_index:int]|1} "
666                         L"{[fullscreen:FULLSCREEN]} "
667                         L"{[key_only:KEY_ONLY]} "
668                         L"{[non_interactive:NON_INTERACTIVE]} "
669                         L"{[no_auto_deinterlace:NO_AUTO_DEINTERLACE]} "
670                         L"{NAME [name:string]}");
671         sink.para()->text(L"Displays the contents of a channel on screen using OpenGL.");
672         sink.definitions()
673                 ->item(L"screen_index", L"Determines which screen the channel should be displayed on. Defaults to 1.")
674                 ->item(L"fullscreen", L"If specified opens the window in fullscreen.")
675                 ->item(L"key_only", L"Only displays the alpha channel of the video channel if specified.")
676                 ->item(L"non_interactive", L"If specified does not send mouse input to producers on the video channel.")
677                 ->item(L"no_auto_deinterlace", L"If the video mode of the channel is an interlaced mode, specifying this will turn of deinterlacing.")
678                 ->item(L"name", L"Optionally specifies a name of the window to show.");
679         sink.para()->text(L"Examples:");
680         sink.example(L">> ADD 1 SCREEN", L"opens a screen consumer on the default screen.");
681         sink.example(L">> ADD 1 SCREEN 2", L"opens a screen consumer on the screen 2.");
682         sink.example(L">> ADD 1 SCREEN 1 FULLSCREEN", L"opens a screen consumer in fullscreen on screen 1.");
683 }
684
685 spl::shared_ptr<core::frame_consumer> create_consumer(const std::vector<std::wstring>& params, core::interaction_sink* sink)
686 {
687         if (params.size() < 1 || !boost::iequals(params.at(0), L"SCREEN"))
688                 return core::frame_consumer::empty();
689         
690         configuration config;
691                 
692         if (params.size() > 1)
693                 config.screen_index = boost::lexical_cast<int>(params.at(1));
694                 
695         config.windowed                 = !contains_param(L"FULLSCREEN", params);
696         config.key_only                 =  contains_param(L"KEY_ONLY", params);
697         config.interactive              = !contains_param(L"NON_INTERACTIVE", params);
698         config.auto_deinterlace = !contains_param(L"NO_AUTO_DEINTERLACE", params);
699
700         if (contains_param(L"NAME", params))
701                 config.name = get_param(L"NAME", params);
702
703         return spl::make_shared<screen_consumer_proxy>(config, sink);
704 }
705
706 spl::shared_ptr<core::frame_consumer> create_preconfigured_consumer(const boost::property_tree::wptree& ptree, core::interaction_sink* sink) 
707 {
708         configuration config;
709         config.name                             = ptree.get(L"name",                            config.name);
710         config.screen_index             = ptree.get(L"device",                          config.screen_index + 1) - 1;
711         config.windowed                 = ptree.get(L"windowed",                        config.windowed);
712         config.key_only                 = ptree.get(L"key-only",                        config.key_only);
713         config.auto_deinterlace = ptree.get(L"auto-deinterlace",        config.auto_deinterlace);
714         config.vsync                    = ptree.get(L"vsync",                           config.vsync);
715         config.interactive              = ptree.get(L"interactive",                     config.interactive);
716
717         auto stretch_str = ptree.get(L"stretch", L"default");
718         if(stretch_str == L"uniform")
719                 config.stretch = screen::stretch::uniform;
720         else if(stretch_str == L"uniform_to_fill")
721                 config.stretch = screen::stretch::uniform_to_fill;
722
723         auto aspect_str = ptree.get(L"aspect-ratio", L"default");
724         if(aspect_str == L"16:9")
725                 config.aspect = configuration::aspect_ratio::aspect_16_9;
726         else if(aspect_str == L"4:3")
727                 config.aspect = configuration::aspect_ratio::aspect_4_3;
728         
729         return spl::make_shared<screen_consumer_proxy>(config, sink);
730 }
731
732 }}