X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=modules%2Fogl%2Fconsumer%2Fogl_consumer.cpp;h=76c06055c46b9847f3f6e908c845260b33beb6da;hb=4bcb75c19cd3c8d35f292e456432a5ffbc5e75d8;hp=24ee295a2fa5e3664ff07c8a489bda66b0e7ce2b;hpb=ff6d30a547b05bcc55d2c8201e70bb016c7c8afd;p=casparcg diff --git a/modules/ogl/consumer/ogl_consumer.cpp b/modules/ogl/consumer/ogl_consumer.cpp index 24ee295a2..76c06055c 100644 --- a/modules/ogl/consumer/ogl_consumer.cpp +++ b/modules/ogl/consumer/ogl_consumer.cpp @@ -18,100 +18,106 @@ * */ -#define NOMINMAX - -#include -#include -#include - #include "ogl_consumer.h" -#include -#include +#include +#include +#include #include -#include +#include #include -#include +#include #include +#include + +#include +#include +#include +#include +#include +#include #include +#include +#include + #include #include namespace caspar { + +enum stretch +{ + none, + uniform, + fill, + uniform_to_fill +}; -struct ogl_consumer::implementation : boost::noncopyable +struct ogl_consumer : boost::noncopyable { - timer clock_; - boost::unique_future active_; - - float width_; - float height_; + core::video_format_desc format_desc_; + + GLuint texture_; + + float width_; + float height_; + + const stretch stretch_; - GLuint texture_; - std::array pbos_; + const bool windowed_; + unsigned int screen_x_; + unsigned int screen_y_; + unsigned int screen_width_; + unsigned int screen_height_; + const unsigned int screen_index_; - const bool windowed_; - unsigned int screen_width_; - unsigned int screen_height_; - const unsigned int screen_index_; - - const stretch stretch_; - core::video_format_desc format_desc_; + size_t square_width_; + size_t square_height_; - sf::Window window_; + sf::Window window_; safe_ptr graph_; - timer perf_timer_; + boost::timer perf_timer_; - size_t square_width_; - size_t square_height_; + boost::circular_buffer> input_buffer_; + tbb::concurrent_bounded_queue> frame_buffer_; - executor executor_; + boost::thread thread_; + tbb::atomic is_running_; public: - implementation(unsigned int screen_index, stretch stretch, bool windowed) - : stretch_(stretch) - , windowed_(windowed) + ogl_consumer(unsigned int screen_index, stretch stretch, bool windowed, const core::video_format_desc& format_desc) + : format_desc_(format_desc) , texture_(0) - , screen_index_(screen_index) + , stretch_(stretch) + , windowed_(windowed) + , screen_index_(screen_index) + , screen_width_(format_desc.width) + , screen_height_(format_desc.height) + , square_width_(format_desc.width) + , square_height_(format_desc.height) , graph_(diagnostics::create_graph(narrow(print()))) - , executor_(print()) + , input_buffer_(core::consumer_buffer_depth()-1) { + frame_buffer_.set_capacity(2); + graph_->add_guide("frame-time", 0.5); graph_->set_color("frame-time", diagnostics::color(1.0f, 0.0f, 0.0f)); - } - - ~implementation() - { - CASPAR_LOG(info) << print() << L" Shutting down."; - } - - void initialize(const core::video_format_desc& format_desc) - { - if(!GLEE_VERSION_2_1) - BOOST_THROW_EXCEPTION(not_supported() << msg_info("Missing OpenGL 2.1 support.")); - - format_desc_ = format_desc; - - square_width_ = format_desc_.width; - square_height_ = format_desc_.height; - + graph_->set_color("dropped-frame", diagnostics::color(0.3f, 0.6f, 0.3f)); + if(format_desc_.format == core::video_format::pal) { - square_width_ = 768; - square_height_ = 576; + square_width_ = 768; + square_height_ = 576; } else if(format_desc_.format == core::video_format::ntsc) { - square_width_ = 720; - square_height_ = 547; + square_width_ = 720; + square_height_ = 547; } - - screen_width_ = format_desc.width; - screen_height_ = format_desc.height; -#ifdef _WIN32 + DISPLAY_DEVICE d_device; memset(&d_device, 0, sizeof(d_device)); d_device.cb = sizeof(d_device); @@ -133,52 +139,143 @@ public: if(!EnumDisplaySettings(displayDevices[screen_index_].DeviceName, ENUM_CURRENT_SETTINGS, &devmode)) BOOST_THROW_EXCEPTION(invalid_operation() << arg_name_info("screen_index") << msg_info(narrow(print()) + " EnumDisplaySettings")); - screen_width_ = windowed_ ? square_width_ : devmode.dmPelsWidth; - screen_height_ = windowed_ ? square_height_ : devmode.dmPelsHeight; -#else - if(!windowed) - BOOST_THROW_EXCEPTION(not_supported() << msg_info(narrow(print() + " doesn't support non-Win32 fullscreen")); - - if(screen_index != 0) - CASPAR_LOG(warning) << print() << " only supports screen_index=0 for non-Win32"; -#endif - executor_.start(); - executor_.invoke([=] - { - window_.Create(sf::VideoMode(screen_width_, screen_height_, 32), narrow(print()), windowed_ ? sf::Style::Resize : sf::Style::Fullscreen); - window_.ShowMouseCursor(false); - window_.SetPosition(devmode.dmPosition.x, devmode.dmPosition.y); - window_.SetSize(screen_width_, screen_height_); - window_.SetActive(); - GL(glEnable(GL_TEXTURE_2D)); - GL(glDisable(GL_DEPTH_TEST)); - GL(glClearColor(0.0, 0.0, 0.0, 0.0)); - GL(glViewport(0, 0, format_desc_.width, format_desc_.height)); - glLoadIdentity(); + screen_x_ = devmode.dmPosition.x; + screen_y_ = devmode.dmPosition.y; + screen_width_ = windowed_ ? square_width_ : devmode.dmPelsWidth; + screen_height_ = windowed_ ? square_height_ : devmode.dmPelsHeight; + + is_running_ = true; + thread_ = boost::thread([this]{run();}); + } + + ~ogl_consumer() + { + is_running_ = false; + frame_buffer_.try_push(make_safe()); + thread_.join(); + } + + void init() + { + if(!GLEW_VERSION_2_1) + BOOST_THROW_EXCEPTION(not_supported() << msg_info("Missing OpenGL 2.1 support.")); + + window_.Create(sf::VideoMode(screen_width_, screen_height_, 32), narrow(print()), windowed_ ? sf::Style::Resize : sf::Style::Fullscreen); + window_.ShowMouseCursor(false); + window_.SetPosition(screen_x_, screen_y_); + window_.SetSize(screen_width_, screen_height_); + window_.SetActive(); + GL(glEnable(GL_TEXTURE_2D)); + GL(glDisable(GL_DEPTH_TEST)); + GL(glClearColor(0.0, 0.0, 0.0, 0.0)); + GL(glViewport(0, 0, format_desc_.width, format_desc_.height)); + GL(glLoadIdentity()); - calculate_aspect(); + calculate_aspect(); - glGenTextures(1, &texture_); - glBindTexture(GL_TEXTURE_2D, texture_); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP); - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, format_desc_.width, format_desc_.height, 0, GL_BGRA, GL_UNSIGNED_BYTE, 0); - glBindTexture(GL_TEXTURE_2D, 0); + GL(glGenTextures(1, &texture_)); + GL(glBindTexture(GL_TEXTURE_2D, texture_)); + GL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST)); + GL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST)); + GL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP)); + GL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP)); + GL(glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, format_desc_.width, format_desc_.height, 0, GL_BGRA, GL_UNSIGNED_BYTE, 0)); + GL(glBindTexture(GL_TEXTURE_2D, 0)); - GL(glGenBuffers(2, pbos_.data())); + CASPAR_LOG(info) << print() << " Sucessfully Initialized."; + } + + void uninit() + { + if(texture_) + glDeleteTextures(1, &texture_); + + CASPAR_LOG(info) << print() << " Sucessfully Uninitialized."; + } + + void run() + { + try + { + init(); + + while(is_running_) + { + try + { + perf_timer_.restart(); + + sf::Event e; + while(window_.GetEvent(e)) + { + if(e.Type == sf::Event::Resized) + calculate_aspect(); + } - glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, pbos_[0]); - glBufferDataARB(GL_PIXEL_UNPACK_BUFFER_ARB, format_desc_.size, 0, GL_STREAM_DRAW_ARB); - glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, pbos_[1]); - glBufferDataARB(GL_PIXEL_UNPACK_BUFFER_ARB, format_desc_.size, 0, GL_STREAM_DRAW_ARB); - glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0); - }); - active_ = executor_.begin_invoke([]{}); - CASPAR_LOG(info) << print() << " Sucessfully initialized."; + safe_ptr frame; + frame_buffer_.pop(frame); + render(frame); + + window_.Display(); + + graph_->update_value("frame-time", static_cast(perf_timer_.elapsed()*format_desc_.fps*0.5)); + } + catch(...) + { + CASPAR_LOG_CURRENT_EXCEPTION(); + is_running_ = false; + } + } + + uninit(); + } + catch(...) + { + CASPAR_LOG_CURRENT_EXCEPTION(); + } + } + + const core::video_format_desc& get_video_format_desc() const + { + return format_desc_; } + void render(const safe_ptr& frame) + { + if(frame->image_data().empty()) + return; + + GL(glBindTexture(GL_TEXTURE_2D, texture_)); + + GL(glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, format_desc_.width, format_desc_.height, GL_BGRA, GL_UNSIGNED_BYTE, frame->image_data().begin())); + + GL(glClear(GL_COLOR_BUFFER_BIT)); + glBegin(GL_QUADS); + glTexCoord2f(0.0f, 1.0f); glVertex2f(-width_, -height_); + glTexCoord2f(1.0f, 1.0f); glVertex2f( width_, -height_); + glTexCoord2f(1.0f, 0.0f); glVertex2f( width_, height_); + glTexCoord2f(0.0f, 0.0f); glVertex2f(-width_, height_); + glEnd(); + + glBindTexture(GL_TEXTURE_2D, 0); + } + + void send(const safe_ptr& frame) + { + input_buffer_.push_back(frame); + + if(input_buffer_.full()) + { + if(!frame_buffer_.try_push(input_buffer_.front())) + graph_->add_tag("dropped-frame"); + } + } + + std::wstring print() const + { + return L"ogl[" + boost::lexical_cast(screen_index_) + L"|" + format_desc_.name + L"]"; + } + void calculate_aspect() { if(windowed_) @@ -234,83 +331,64 @@ public: return std::make_pair(width, height); } +}; - void render(const safe_ptr& frame) - { - glBindTexture(GL_TEXTURE_2D, texture_); - glBindBuffer(GL_PIXEL_UNPACK_BUFFER, pbos_[0]); - - glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, format_desc_.width, format_desc_.height, GL_BGRA, GL_UNSIGNED_BYTE, 0); - - glBindBuffer(GL_PIXEL_UNPACK_BUFFER, pbos_[1]); - glBufferData(GL_PIXEL_UNPACK_BUFFER, format_desc_.size, 0, GL_STREAM_DRAW); +struct ogl_consumer_proxy : public core::frame_consumer +{ + size_t screen_index_; + caspar::stretch stretch_; + bool windowed_; + bool key_only_; - auto ptr = glMapBuffer(GL_PIXEL_UNPACK_BUFFER, GL_WRITE_ONLY); - if(ptr) - { - std::copy(frame->image_data().begin(), frame->image_data().end(), reinterpret_cast(ptr)); - glUnmapBuffer(GL_PIXEL_UNPACK_BUFFER); // release the mapped buffer - } + std::unique_ptr consumer_; - glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); - - GL(glClear(GL_COLOR_BUFFER_BIT)); - glBegin(GL_QUADS); - glTexCoord2f(0.0f, 1.0f); glVertex2f(-width_, -height_); - glTexCoord2f(1.0f, 1.0f); glVertex2f( width_, -height_); - glTexCoord2f(1.0f, 0.0f); glVertex2f( width_, height_); - glTexCoord2f(0.0f, 0.0f); glVertex2f(-width_, height_); - glEnd(); - - glBindTexture(GL_TEXTURE_2D, 0); +public: - std::rotate(pbos_.begin(), pbos_.begin() + 1, pbos_.end()); + ogl_consumer_proxy(size_t screen_index, stretch stretch, bool windowed, bool key_only) + : screen_index_(screen_index) + , stretch_(stretch) + , windowed_(windowed) + , key_only_(key_only){} + + virtual void initialize(const core::video_format_desc& format_desc) + { + consumer_.reset(new ogl_consumer(screen_index_, stretch_, windowed_, format_desc)); } - - void send(const safe_ptr& frame) + + virtual bool send(const safe_ptr& frame) { - active_.get(); - active_ = executor_.begin_invoke([=] - { - perf_timer_.reset(); - sf::Event e; - while(window_.GetEvent(e)) - { - if(e.Type == sf::Event::Resized) - calculate_aspect(); - } - render(frame); - window_.Display(); - graph_->update_value("frame-time", static_cast(perf_timer_.elapsed()/format_desc_.interval*0.5)); - }); + consumer_->send(frame); + return true; + } + + virtual std::wstring print() const + { + return consumer_->print(); } - std::wstring print() const + virtual bool key_only() const { - return L"ogl[" + boost::lexical_cast(screen_index_) + L"]"; + return key_only_; } - size_t buffer_depth() const{return 2;} -}; + virtual bool has_synchronization_clock() const + { + return false; + } -ogl_consumer::ogl_consumer(ogl_consumer&& other) : impl_(std::move(other.impl_)){} -ogl_consumer::ogl_consumer(unsigned int screen_index, stretch stretch, bool windowed) : impl_(new implementation(screen_index, stretch, windowed)){} -void ogl_consumer::send(const safe_ptr& frame){impl_->send(frame);} -size_t ogl_consumer::buffer_depth() const{return impl_->buffer_depth();} -void ogl_consumer::initialize(const core::video_format_desc& format_desc) -{ - impl_.reset(new implementation(impl_->screen_index_, impl_->stretch_, impl_->windowed_)); - impl_->initialize(format_desc); -} -std::wstring ogl_consumer::print() const {return impl_->print();} + virtual const core::video_format_desc& get_video_format_desc() const + { + return consumer_->get_video_format_desc(); + } +}; safe_ptr create_ogl_consumer(const std::vector& params) { - if(params.size() < 1 || params[0] != L"OGL") + if(params.size() < 1 || params[0] != L"SCREEN") return core::frame_consumer::empty(); - unsigned int screen_index = 0; + size_t screen_index = 0; stretch stretch = stretch::fill; bool windowed = true; @@ -320,7 +398,26 @@ safe_ptr create_ogl_consumer(const std::vector 2) windowed = lexical_cast_or_default(params[3], windowed); - return make_safe(screen_index, stretch, windowed); + bool key_only = std::find(params.begin(), params.end(), L"KEY_ONLY") != params.end(); + + return make_safe(screen_index, stretch, windowed, key_only); +} + +safe_ptr create_ogl_consumer(const boost::property_tree::ptree& ptree) +{ + size_t screen_index = ptree.get("device", 0); + bool windowed =ptree.get("windowed", true); + + stretch stretch = stretch::fill; + auto key_str = ptree.get("stretch", "default"); + if(key_str == "uniform") + stretch = stretch::uniform; + else if(key_str == "uniform_to_fill") + stretch = stretch::uniform_to_fill; + + bool key_only = ptree.get("key-only", false); + + return make_safe(screen_index, stretch, windowed, key_only); } } \ No newline at end of file