X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=modules%2Fogl%2Fconsumer%2Fogl_consumer.cpp;h=3199253fdcde5945ad3fbf5c4fb6e57c5d40a7e8;hb=a4ee25cab50a83d43220cef77d50c78fc16951ec;hp=4c8b8caf89b577244ac1f0030e47bf77a7ee16f2;hpb=745f022a44f0d347e56acbbd597ff71e06eab894;p=casparcg diff --git a/modules/ogl/consumer/ogl_consumer.cpp b/modules/ogl/consumer/ogl_consumer.cpp index 4c8b8caf8..3199253fd 100644 --- a/modules/ogl/consumer/ogl_consumer.cpp +++ b/modules/ogl/consumer/ogl_consumer.cpp @@ -18,92 +18,138 @@ * */ -#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 + +#include + #include -#include +#include + +#if defined(_MSC_VER) +#pragma warning (push) +#pragma warning (disable : 4244) +#endif +extern "C" +{ + #define __STDC_CONSTANT_MACROS + #define __STDC_LIMIT_MACROS + #include + #include +} +#if defined(_MSC_VER) +#pragma warning (pop) +#endif namespace caspar { + +enum stretch +{ + none, + uniform, + fill, + uniform_to_fill +}; -struct ogl_consumer::implementation : boost::noncopyable +struct ogl_consumer : boost::noncopyable { - timer clock_; - printer parent_printer_; - boost::unique_future active_; - - float wratio_; - float hratio_; - - float wSize_; - float hSize_; - - GLuint texture_; - std::array pbos_; - - const bool windowed_; - unsigned int screen_width_; - unsigned int screen_height_; - unsigned int screen_x_; - unsigned int screen_y_; - const unsigned int screen_index_; - - const stretch stretch_; core::video_format_desc format_desc_; - sf::Window window_; + GLuint texture_; + std::vector pbos_; + + float width_; + float height_; + + const stretch stretch_; + + const bool windowed_; + unsigned int screen_x_; + unsigned int screen_y_; + unsigned int screen_width_; + unsigned int screen_height_; + const unsigned int screen_index_; + + size_t square_width_; + size_t square_height_; + + sf::Window window_; safe_ptr graph_; - timer perf_timer_; + boost::timer perf_timer_; + + boost::circular_buffer> input_buffer_; + tbb::concurrent_bounded_queue> frame_buffer_; - executor executor_; + boost::thread thread_; + tbb::atomic is_running_; + + + filter filter_; + + const bool key_only_; 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, bool key_only) + : format_desc_(format_desc) , texture_(0) - , screen_x_(0) - , screen_y_(0) - , screen_index_(screen_index) + , pbos_(2, 0) + , 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) + , key_only_(key_only) + , filter_(format_desc.field_mode == core::field_mode::progressive ? L"" : L"YADIF=0:-1", boost::assign::list_of(PIX_FMT_BGRA)) { - graph_->add_guide("frame-time", 0.5); - graph_->set_color("frame-time", diagnostics::color(1.0f, 0.0f, 0.0f)); - } + if(format_desc.field_mode != core::field_mode::progressive) + CASPAR_LOG(info) << print() << L" Deinterlacer enabled."; - ~implementation() - { - CASPAR_LOG(info) << print() << L" Shutting down."; - } + frame_buffer_.set_capacity(2); - void initialize(const core::video_format_desc& format_desc, const printer& parent_printer) - { - if(!GLEE_VERSION_2_1) - BOOST_THROW_EXCEPTION(not_supported() << msg_info("Missing OpenGL 2.1 support.")); + graph_->add_guide("frame-time", 0.5); + graph_->set_color("frame-time", diagnostics::color(1.0f, 0.0f, 0.0f)); + 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; + } + else if(format_desc_.format == core::video_format::ntsc) + { + square_width_ = 720; + square_height_ = 547; + } - format_desc_ = format_desc; - parent_printer_ = parent_printer; - - 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); @@ -125,115 +171,184 @@ 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_ ? format_desc_.width : devmode.dmPelsWidth; - screen_height_ = windowed_ ? format_desc_.height : devmode.dmPelsHeight; - screen_x_ = devmode.dmPosition.x; - screen_y_ = devmode.dmPosition.y; -#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(format_desc_.width, format_desc_.height, 32), narrow(print()), windowed_ ? sf::Style::Titlebar : 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)); - 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()); - wratio_ = static_cast(format_desc_.width)/static_cast(format_desc_.width); - hratio_ = static_cast(format_desc_.height)/static_cast(format_desc_.height); - - std::pair target_ratio = None(); - if(stretch_ == fill) - target_ratio = Fill(); - else if(stretch_ == uniform) - target_ratio = Uniform(); - else if(stretch_ == uniform_to_fill) - target_ratio = UniformToFill(); - - wSize_ = target_ratio.first; - hSize_ = target_ratio.second; + 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())); - GL(glGenBuffers(2, pbos_.data())); - - 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."; - } - - std::pair None() - { - float width = static_cast(format_desc_.width)/static_cast(screen_width_); - float height = static_cast(format_desc_.height)/static_cast(screen_height_); + 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); - return std::make_pair(width, height); + CASPAR_LOG(info) << print() << " Sucessfully Initialized."; } - std::pair Uniform() - { - float aspect = static_cast(format_desc_.width)/static_cast(format_desc_.height); - float width = std::min(1.0f, static_cast(screen_height_)*aspect/static_cast(screen_width_)); - float height = static_cast(screen_width_*width)/static_cast(screen_height_*aspect); + void uninit() + { + if(texture_) + glDeleteTextures(1, &texture_); - return std::make_pair(width, height); + BOOST_FOREACH(auto& pbo, pbos_) + { + if(pbo) + glDeleteBuffers(1, &pbo); + } + + CASPAR_LOG(info) << print() << " Sucessfully Uninitialized."; } - std::pair Fill() + void run() { - return std::make_pair(1.0f, 1.0f); + try + { + init(); + + while(is_running_) + { + try + { + perf_timer_.restart(); + + sf::Event e; + while(window_.GetEvent(e)) + { + if(e.Type == sf::Event::Resized) + calculate_aspect(); + } + + 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(); + } } - - std::pair UniformToFill() + + const core::video_format_desc& get_video_format_desc() const { - float wr = static_cast(format_desc_.width)/static_cast(screen_width_); - float hr = static_cast(format_desc_.height)/static_cast(screen_height_); - float r_inv = 1.0f/std::min(wr, hr); - - float width = wr*r_inv; - float height = hr*r_inv; + return format_desc_; + } - return std::make_pair(width, height); + safe_ptr get_av_frame() + { + safe_ptr av_frame(avcodec_alloc_frame(), av_free); + avcodec_get_frame_defaults(av_frame.get()); + + av_frame->linesize[0] = format_desc_.width*4; + av_frame->format = PIX_FMT_BGRA; + av_frame->width = format_desc_.width; + 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; + + return av_frame; } - void render(const safe_ptr& frame) + void render(const safe_ptr& frame) { + if(frame->image_data().empty()) + return; + + auto av_frame = get_av_frame(); + av_frame->data[0] = const_cast(frame->image_data().begin()); + + auto frames = filter_.execute(av_frame); + + if(frames.empty()) + return; + + av_frame = frames[0]; + + if(av_frame->linesize[0] != static_cast(format_desc_.width*4)) + { + const uint8_t *src_data[4] = {0}; + memcpy(const_cast(&src_data[0]), av_frame->data, 4); + const int src_linesizes[4] = {0}; + memcpy(const_cast(&src_linesizes[0]), av_frame->linesize, 4); + + auto av_frame2 = get_av_frame(); + av_image_alloc(av_frame2->data, av_frame2->linesize, av_frame2->width, av_frame2->height, PIX_FMT_BGRA, 16); + av_frame = safe_ptr(av_frame2.get(), [=](AVFrame*) + { + av_freep(&av_frame2->data[0]); + }); + + av_image_copy(av_frame2->data, av_frame2->linesize, src_data, src_linesizes, PIX_FMT_BGRA, av_frame2->width, av_frame2->height); + } + glBindTexture(GL_TEXTURE_2D, texture_); - glBindBuffer(GL_PIXEL_UNPACK_BUFFER, pbos_[0]); + 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); 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)); + if(key_only_) + fast_memshfl(reinterpret_cast(ptr), av_frame->data[0], frame->image_data().size(), 0x0F0F0F0F, 0x0B0B0B0B, 0x07070707, 0x03030303); + else + fast_memcpy(reinterpret_cast(ptr), av_frame->data[0], frame->image_data().size()); + glUnmapBuffer(GL_PIXEL_UNPACK_BUFFER); // release the mapped buffer } @@ -241,54 +356,141 @@ public: GL(glClear(GL_COLOR_BUFFER_BIT)); glBegin(GL_QUADS); - glTexCoord2f(0.0f, hratio_); glVertex2f(-wSize_, -hSize_); - glTexCoord2f(wratio_, hratio_); glVertex2f( wSize_, -hSize_); - glTexCoord2f(wratio_, 0.0f); glVertex2f( wSize_, hSize_); - glTexCoord2f(0.0f, 0.0f); glVertex2f(-wSize_, hSize_); + 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); std::rotate(pbos_.begin(), pbos_.begin() + 1, pbos_.end()); + } + + void send(const safe_ptr& frame) + { + input_buffer_.push_back(frame); - clock_.tick(1.0/format_desc_.fps); + if(input_buffer_.full()) + { + if(!frame_buffer_.try_push(input_buffer_.front())) + graph_->add_tag("dropped-frame"); + } } - void send(const safe_ptr& frame) + std::wstring print() const + { + return L"ogl[" + boost::lexical_cast(screen_index_) + L"|" + format_desc_.name + L"]"; + } + + void calculate_aspect() { - active_.get(); - active_ = executor_.begin_invoke([=] + if(windowed_) { - perf_timer_.reset(); - sf::Event e; - while(window_.GetEvent(e)){} - render(frame); - window_.Display(); - graph_->update_value("frame-time", static_cast(perf_timer_.elapsed()/format_desc_.interval*0.5)); - }); + screen_height_ = window_.GetHeight(); + screen_width_ = window_.GetWidth(); + } + + GL(glViewport(0, 0, screen_width_, screen_height_)); + + std::pair target_ratio = None(); + if(stretch_ == fill) + target_ratio = Fill(); + else if(stretch_ == uniform) + target_ratio = Uniform(); + else if(stretch_ == uniform_to_fill) + target_ratio = UniformToFill(); + + width_ = target_ratio.first; + height_ = target_ratio.second; } + + std::pair None() + { + float width = static_cast(square_width_)/static_cast(screen_width_); + float height = static_cast(square_height_)/static_cast(screen_height_); - std::wstring print() const + return std::make_pair(width, height); + } + + std::pair Uniform() + { + float aspect = static_cast(square_width_)/static_cast(square_height_); + float width = std::min(1.0f, static_cast(screen_height_)*aspect/static_cast(screen_width_)); + float height = static_cast(screen_width_*width)/static_cast(screen_height_*aspect); + + return std::make_pair(width, height); + } + + std::pair Fill() { - return (parent_printer_ ? parent_printer_() + L"/" : L"") + L"ogl[" + boost::lexical_cast(screen_index_) + L"]"; + return std::make_pair(1.0f, 1.0f); } - size_t buffer_depth() const{return 2;} + std::pair UniformToFill() + { + float wr = static_cast(square_width_)/static_cast(screen_width_); + float hr = static_cast(square_height_)/static_cast(screen_height_); + float r_inv = 1.0f/std::min(wr, hr); + + float width = wr*r_inv; + float height = hr*r_inv; + + return std::make_pair(width, height); + } }; -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, const printer& parent_printer){impl_->initialize(format_desc, parent_printer);} -std::wstring ogl_consumer::print() const {return impl_->print();} + +struct ogl_consumer_proxy : public core::frame_consumer +{ + size_t screen_index_; + caspar::stretch stretch_; + bool windowed_; + bool key_only_; + + std::unique_ptr consumer_; + +public: + + 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, key_only_)); + } + + virtual bool send(const safe_ptr& frame) + { + consumer_->send(frame); + return true; + } + + virtual std::wstring print() const + { + return consumer_->print(); + } + + virtual bool has_synchronization_clock() const + { + return false; + } + + 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; @@ -298,7 +500,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