]> git.sesse.net Git - casparcg/blobdiff - modules/screen/consumer/screen_consumer.cpp
Use fast_memcpy in more places
[casparcg] / modules / screen / consumer / screen_consumer.cpp
index e3053471f316f6ec8e9a4f1147c246d9f8cbb0a0..ae8960b4c48af701a14f1ab0bf78f3c96ef7cfa8 100644 (file)
 #include <common/memory.h>
 #include <common/array.h>
 #include <common/memshfl.h>
+#include <common/memcpy.h>
 #include <common/utf.h>
 #include <common/prec_timer.h>
 #include <common/future.h>
 #include <common/timer.h>
 #include <common/param.h>
+#include <common/os/general_protection_fault.h>
+#include <common/scope_exit.h>
 
 //#include <windows.h>
 
@@ -44,6 +47,8 @@
 #include <core/frame/frame.h>
 #include <core/consumer/frame_consumer.h>
 #include <core/interaction/interaction_sink.h>
+#include <core/help/help_sink.h>
+#include <core/help/help_repository.h>
 
 #include <boost/circular_buffer.hpp>
 #include <boost/lexical_cast.hpp>
@@ -94,7 +99,7 @@ struct configuration
                aspect_invalid,
        };
                
-       std::wstring    name                            = L"ogl";
+       std::wstring    name                            = L"Screen consumer";
        int                             screen_index            = 0;
        screen::stretch stretch                         = screen::stretch::fill;
        bool                    windowed                        = true;
@@ -103,6 +108,7 @@ struct configuration
        aspect_ratio    aspect                          = aspect_ratio::aspect_invalid;
        bool                    vsync                           = true;
        bool                    interactive                     = true;
+       bool                    borderless                      = false;
 };
 
 struct screen_consumer : boost::noncopyable
@@ -124,6 +130,8 @@ struct screen_consumer : boost::noncopyable
        int                                                                                                     square_height_  = format_desc_.square_height;
 
        sf::Window                                                                                      window_;
+       tbb::atomic<bool>                                                                       polling_event_;
+       std::int64_t                                                                            pts_;
 
        spl::shared_ptr<diagnostics::graph>                                     graph_;
        caspar::timer                                                                           perf_timer_;
@@ -136,6 +144,7 @@ struct screen_consumer : boost::noncopyable
 
        boost::thread                                                                           thread_;
        tbb::atomic<bool>                                                                       is_running_;
+       tbb::atomic<int64_t>                                                            current_presentation_age_;
 
        ffmpeg::filter                                                                          filter_;
 public:
@@ -147,6 +156,7 @@ public:
                : config_(config)
                , format_desc_(format_desc)
                , channel_index_(channel_index)
+               , pts_(0)
                , sink_(sink)
                , filter_([&]() -> ffmpeg::filter
                {                       
@@ -166,7 +176,7 @@ public:
                                sample_aspect_ratio,
                                AV_PIX_FMT_BGRA,
                                { AV_PIX_FMT_BGRA },
-                               format_desc.field_mode == core::field_mode::progressive || !config.auto_deinterlace ? "" : "YADIF=1:-1");
+                               format_desc.field_mode == core::field_mode::progressive || !config.auto_deinterlace ? "" : "format=pix_fmts=gbrp,YADIF=1:-1");
                }())
        {               
                if (format_desc_.format == core::video_format::ntsc && config_.aspect == configuration::aspect_ratio::aspect_4_3)
@@ -210,7 +220,9 @@ public:
                screen_width_   = square_width_;
                screen_height_  = square_height_;
                
+               polling_event_ = false;
                is_running_ = true;
+               current_presentation_age_ = 0;
                thread_ = boost::thread([this]{run();});
        }
        
@@ -223,7 +235,12 @@ public:
 
        void init()
        {
-               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);
+               auto window_style = config_.borderless
+                       ? sf::Style::None
+                       : (config_.windowed
+                               ? sf::Style::Resize | sf::Style::Close
+                               : sf::Style::Fullscreen);
+               window_.create(sf::VideoMode(screen_width_, screen_height_, 32), u8(print()), window_style);
                window_.setMouseCursorVisible(config_.interactive);
                window_.setPosition(sf::Vector2i(screen_x_, screen_y_));
                window_.setSize(sf::Vector2u(screen_width_, screen_height_));
@@ -277,8 +294,6 @@ public:
                        else
                                wglSwapIntervalEXT(0);
                }*/
-
-               CASPAR_LOG(info) << print() << " Successfully Initialized.";
        }
 
        void uninit()
@@ -295,6 +310,8 @@ public:
 
        void run()
        {
+               ensure_gpf_handler_installed_for_thread("screen-consumer-thread");
+
                try
                {
                        init();
@@ -303,8 +320,18 @@ public:
                        {                       
                                try
                                {
-                                       sf::Event e;            
-                                       while(window_.pollEvent(e))
+                                       auto poll_event = [this](sf::Event& e)
+                                       {
+                                               polling_event_ = true;
+                                               CASPAR_SCOPE_EXIT
+                                               {
+                                                       polling_event_ = false;
+                                               };
+                                               return window_.pollEvent(e);
+                                       };
+
+                                       sf::Event e;
+                                       while(poll_event(e))
                                        {
                                                if (e.type == sf::Event::Resized)
                                                        calculate_aspect();
@@ -349,7 +376,7 @@ public:
                                                }
                                        }
                        
-                                       auto frame = core::const_frame::empty();
+                                       core::const_frame frame;
                                        frame_buffer_.pop(frame);
 
                                        render_and_draw_frame(frame);
@@ -360,6 +387,7 @@ public:
 
                                        window_.Display();*/
 
+                                       current_presentation_age_ = frame.get_age_millis();
                                        graph_->set_value("tick-time", tick_timer_.elapsed()*format_desc_.fps*0.5);     
                                        tick_timer_.restart();
                                }
@@ -398,7 +426,7 @@ public:
 
        spl::shared_ptr<AVFrame> get_av_frame()
        {               
-               spl::shared_ptr<AVFrame> av_frame(avcodec_alloc_frame(), av_free);      
+               spl::shared_ptr<AVFrame> av_frame(av_frame_alloc(), [](AVFrame* p) { av_frame_free(&p); });
                avcodec_get_frame_defaults(av_frame.get());
                                                
                av_frame->linesize[0]           = format_desc_.width*4;                 
@@ -407,13 +435,14 @@ public:
                av_frame->height                        = format_desc_.height;
                av_frame->interlaced_frame      = format_desc_.field_mode != core::field_mode::progressive;
                av_frame->top_field_first       = format_desc_.field_mode == core::field_mode::upper ? 1 : 0;
+               av_frame->pts                           = pts_++;
 
                return av_frame;
        }
 
-       void render_and_draw_frame(core::const_frame frame)
+       void render_and_draw_frame(core::const_frame input_frame)
        {
-               if(static_cast<size_t>(frame.image_data().size()) != format_desc_.size)
+               if(static_cast<size_t>(input_frame.image_data().size()) != format_desc_.size)
                        return;
 
                if(screen_width_ == 0 && screen_height_ == 0)
@@ -421,30 +450,31 @@ public:
                                        
                perf_timer_.restart();
                auto av_frame = get_av_frame();
-               av_frame->data[0] = const_cast<uint8_t*>(frame.image_data().begin());
+               av_frame->data[0] = const_cast<uint8_t*>(input_frame.image_data().begin());
 
                filter_.push(av_frame);
-               auto frames = filter_.poll_all();
+               auto frame = filter_.poll();
 
-               if (frames.empty())
+               if (!frame)
                        return;
 
-               if (frames.size() == 1)
+               if (!filter_.is_double_rate())
                {
-                       render(frames[0]);
+                       render(spl::make_shared_ptr(frame));
                        graph_->set_value("frame-time", perf_timer_.elapsed() * format_desc_.fps * 0.5);
 
                        wait_for_vblank_and_display(); // progressive frame
                }
-               else if (frames.size() == 2)
+               else
                {
-                       render(frames[0]);
+                       render(spl::make_shared_ptr(frame));
                        double perf_elapsed = perf_timer_.elapsed();
 
                        wait_for_vblank_and_display(); // field1
 
                        perf_timer_.restart();
-                       render(frames[1]);
+                       frame = filter_.poll();
+                       render(spl::make_shared_ptr(frame));
                        perf_elapsed += perf_timer_.elapsed();
                        graph_->set_value("frame-time", perf_elapsed * format_desc_.fps * 0.5);
 
@@ -474,12 +504,8 @@ public:
                                });
                        }
                        else
-                       {       
-                               tbb::parallel_for(tbb::blocked_range<int>(0, format_desc_.height), [&](const tbb::blocked_range<int>& r)
-                               {
-                                       for(int n = r.begin(); n != r.end(); ++n)
-                                               A_memcpy(ptr+n*format_desc_.width*4, av_frame->data[0]+n*av_frame->linesize[0], format_desc_.width*4);
-                               });
+                       {
+                               fast_memcpy(ptr, av_frame->data[0], format_desc_.size);
                        }
                        
                        GL(glUnmapBuffer(GL_PIXEL_UNPACK_BUFFER)); // release the mapped buffer
@@ -503,8 +529,8 @@ public:
 
        std::future<bool> send(core::const_frame frame)
        {
-               if(!frame_buffer_.try_push(frame))
-                       graph_->set_tag("dropped-frame");
+               if(!frame_buffer_.try_push(frame) && !polling_event_)
+                       graph_->set_tag(diagnostics::tag_severity::WARNING, "dropped-frame");
 
                return make_ready_future(is_running_.load());
        }
@@ -516,7 +542,7 @@ public:
 
        std::wstring print() const
        {       
-               return config_.name + channel_and_format();
+               return config_.name + L" " + channel_and_format();
        }
        
        void calculate_aspect()
@@ -594,12 +620,17 @@ public:
        
        // frame_consumer
 
-       void initialize(const core::video_format_desc& format_desc, int channel_index) override
+       void initialize(const core::video_format_desc& format_desc, const core::audio_channel_layout&, int channel_index) override
        {
                consumer_.reset();
                consumer_.reset(new screen_consumer(config_, format_desc, channel_index, sink_));
        }
-       
+
+       int64_t presentation_frame_age_millis() const override
+       {
+               return consumer_ ? static_cast<int64_t>(consumer_->current_presentation_age_) : 0;
+       }
+
        std::future<bool> send(core::const_frame frame) override
        {
                return consumer_->send(frame);
@@ -622,6 +653,7 @@ public:
                info.add(L"key-only", config_.key_only);
                info.add(L"windowed", config_.windowed);
                info.add(L"auto-deinterlace", config_.auto_deinterlace);
+               info.add(L"vsync", config_.vsync);
                return info;
        }
 
@@ -646,6 +678,34 @@ public:
        }
 };     
 
+void describe_consumer(core::help_sink& sink, const core::help_repository& repo)
+{
+       sink.short_description(L"Displays the contents of a channel on screen using OpenGL.");
+       sink.syntax(
+                       L"SCREEN "
+                       L"{[screen_index:int]|1} "
+                       L"{[fullscreen:FULLSCREEN]} "
+                       L"{[borderless:BORDERLESS]} "
+                       L"{[key_only:KEY_ONLY]} "
+                       L"{[non_interactive:NON_INTERACTIVE]} "
+                       L"{[no_auto_deinterlace:NO_AUTO_DEINTERLACE]} "
+                       L"{NAME [name:string]}");
+       sink.para()->text(L"Displays the contents of a channel on screen using OpenGL.");
+       sink.definitions()
+               ->item(L"screen_index", L"Determines which screen the channel should be displayed on. Defaults to 1.")
+               ->item(L"fullscreen", L"If specified opens the window in fullscreen.")
+               ->item(L"borderless", L"Makes the window appear without any window decorations.")
+               ->item(L"key_only", L"Only displays the alpha channel of the video channel if specified.")
+               ->item(L"non_interactive", L"If specified does not send mouse input to producers on the video channel.")
+               ->item(L"no_auto_deinterlace", L"If the video mode of the channel is an interlaced mode, specifying this will turn of deinterlacing.")
+               ->item(L"name", L"Optionally specifies a name of the window to show.");
+       sink.para()->text(L"Examples:");
+       sink.example(L">> ADD 1 SCREEN", L"opens a screen consumer on the default screen.");
+       sink.example(L">> ADD 1 SCREEN 2", L"opens a screen consumer on the screen 2.");
+       sink.example(L">> ADD 1 SCREEN 1 FULLSCREEN", L"opens a screen consumer in fullscreen on screen 1.");
+       sink.example(L">> ADD 1 SCREEN 1 BORDERLESS", L"opens a screen consumer without borders/window decorations on screen 1.");
+}
+
 spl::shared_ptr<core::frame_consumer> create_consumer(const std::vector<std::wstring>& params, core::interaction_sink* sink)
 {
        if (params.size() < 1 || !boost::iequals(params.at(0), L"SCREEN"))
@@ -660,6 +720,7 @@ spl::shared_ptr<core::frame_consumer> create_consumer(const std::vector<std::wst
        config.key_only                 =  contains_param(L"KEY_ONLY", params);
        config.interactive              = !contains_param(L"NON_INTERACTIVE", params);
        config.auto_deinterlace = !contains_param(L"NO_AUTO_DEINTERLACE", params);
+       config.borderless               =  contains_param(L"BORDERLESS", params);
 
        if (contains_param(L"NAME", params))
                config.name = get_param(L"NAME", params);
@@ -677,6 +738,7 @@ spl::shared_ptr<core::frame_consumer> create_preconfigured_consumer(const boost:
        config.auto_deinterlace = ptree.get(L"auto-deinterlace",        config.auto_deinterlace);
        config.vsync                    = ptree.get(L"vsync",                           config.vsync);
        config.interactive              = ptree.get(L"interactive",                     config.interactive);
+       config.borderless               = ptree.get(L"borderless",                      config.borderless);
 
        auto stretch_str = ptree.get(L"stretch", L"default");
        if(stretch_str == L"uniform")