X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=modules%2Fogl%2Fconsumer%2Fogl_consumer.cpp;h=98346a7d7955ab68c2ace421f57ac7d81e2038ad;hb=d74b2a2927388498a1ef7dec2f99fc9421fc5e2b;hp=7c3bb3288b53757ff6e118607006eefdf0a455a3;hpb=cd22a45bbd82a0471c223dbc94ad834b4bdb079d;p=casparcg diff --git a/modules/ogl/consumer/ogl_consumer.cpp b/modules/ogl/consumer/ogl_consumer.cpp index 7c3bb3288..98346a7d7 100644 --- a/modules/ogl/consumer/ogl_consumer.cpp +++ b/modules/ogl/consumer/ogl_consumer.cpp @@ -18,29 +18,53 @@ * */ -#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 +#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 { @@ -52,136 +76,299 @@ enum stretch uniform_to_fill }; +struct configuration +{ + size_t screen_index; + stretch stretch; + bool windowed; + bool key_only; + + configuration() + : screen_index(0) + , stretch(fill) + , windowed(true) + , key_only(false) + { + } +}; + struct ogl_consumer : boost::noncopyable { - boost::unique_future active_; - - float width_; - float height_; - - GLuint texture_; - std::array pbos_; - - const bool windowed_; - unsigned int screen_width_; - unsigned int screen_height_; - const unsigned int screen_index_; - - const stretch stretch_; + const configuration config_; core::video_format_desc format_desc_; - sf::Window window_; + GLuint texture_; + std::vector pbos_; + + float width_; + float height_; + unsigned int screen_x_; + unsigned int screen_y_; + unsigned int screen_width_; + unsigned int screen_height_; + size_t square_width_; + size_t square_height_; + + sf::Window window_; - safe_ptr graph_; - boost::timer perf_timer_; + safe_ptr graph_; + 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_; + + + filter filter_; public: - ogl_consumer(unsigned int screen_index, stretch stretch, bool windowed, const core::video_format_desc& format_desc) - : stretch_(stretch) - , windowed_(windowed) + ogl_consumer(const configuration& config, const core::video_format_desc& format_desc) + : config_(config) + , format_desc_(format_desc) , texture_(0) - , screen_index_(screen_index) - , format_desc_(format_desc_) + , pbos_(2, 0) + , screen_width_(format_desc.width) + , screen_height_(format_desc.height) + , square_width_(format_desc.square_width) + , square_height_(format_desc.square_height) , graph_(diagnostics::create_graph(narrow(print()))) - , executor_(print()) + , input_buffer_(core::consumer_buffer_depth()-1) + , filter_(format_desc.field_mode == core::field_mode::progressive ? L"" : L"YADIF=0:-1", boost::assign::list_of(PIX_FMT_BGRA)) { - executor_.set_capacity(3); + 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)); - if(!GLEE_VERSION_2_1) - BOOST_THROW_EXCEPTION(not_supported() << msg_info("Missing OpenGL 2.1 support.")); + graph_->set_color("dropped-frame", diagnostics::color(0.3f, 0.6f, 0.3f)); + + DISPLAY_DEVICE d_device = {sizeof(d_device), 0}; + std::vector displayDevices; + for(int n = 0; EnumDisplayDevices(NULL, n, &d_device, NULL); ++n) + displayDevices.push_back(d_device); + + if(config_.screen_index >= displayDevices.size()) + BOOST_THROW_EXCEPTION(out_of_range() << arg_name_info("screen_index_") << msg_info(narrow(print()))); + + DEVMODE devmode = {}; + if(!EnumDisplaySettings(displayDevices[config_.screen_index].DeviceName, ENUM_CURRENT_SETTINGS, &devmode)) + BOOST_THROW_EXCEPTION(invalid_operation() << arg_name_info("screen_index") << msg_info(narrow(print()) + " EnumDisplaySettings")); + + screen_x_ = devmode.dmPosition.x; + screen_y_ = devmode.dmPosition.y; + screen_width_ = config_.windowed ? square_width_ : devmode.dmPelsWidth; + screen_height_ = config_.windowed ? square_height_ : devmode.dmPelsHeight; - format_desc_ = format_desc; + is_running_ = true; + thread_ = boost::thread([this]{run();}); + } + + ~ogl_consumer() + { + is_running_ = false; + frame_buffer_.try_push(make_safe()); + thread_.join(); + } - square_width_ = format_desc_.width; - square_height_ = format_desc_.height; - - if(format_desc_.format == core::video_format::pal) + 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()), config_.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(); + + 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())); + + 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); + + CASPAR_LOG(info) << print() << " Sucessfully Initialized."; + } + + void uninit() + { + if(texture_) + glDeleteTextures(1, &texture_); + + BOOST_FOREACH(auto& pbo, pbos_) + { + if(pbo) + glDeleteBuffers(1, &pbo); + } + + CASPAR_LOG(info) << print() << " Sucessfully Uninitialized."; + } + + void run() + { + try { - square_width_ = 768; - square_height_ = 576; + 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(); } - else if(format_desc_.format == core::video_format::ntsc) + catch(...) { - square_width_ = 720; - square_height_ = 547; + CASPAR_LOG_CURRENT_EXCEPTION(); } + } + + const core::video_format_desc& get_video_format_desc() const + { + return format_desc_; + } - 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); + 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; + } - std::vector displayDevices; - for(int n = 0; EnumDisplayDevices(NULL, n, &d_device, NULL); ++n) + 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)) { - displayDevices.push_back(d_device); - memset(&d_device, 0, sizeof(d_device)); - d_device.cb = sizeof(d_device); + 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); } - if(screen_index_ >= displayDevices.size()) - BOOST_THROW_EXCEPTION(out_of_range() << arg_name_info("screen_index_") << msg_info(narrow(print()))); - - DEVMODE devmode; - memset(&devmode, 0, sizeof(devmode)); - - 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_.invoke([=] + 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); + + auto ptr = glMapBuffer(GL_PIXEL_UNPACK_BUFFER, GL_WRITE_ONLY); + if(ptr) { - 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(); + if(config_.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 + } + + glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); - 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(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); - }); - CASPAR_LOG(info) << print() << " Sucessfully Initialized."; + 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); + + std::rotate(pbos_.begin(), pbos_.begin() + 1, pbos_.end()); + } + + 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(config_.screen_index) + L"|" + format_desc_.name + L"]"; } void calculate_aspect() { - if(windowed_) + if(config_.windowed) { screen_height_ = window_.GetHeight(); screen_width_ = window_.GetWidth(); @@ -190,11 +377,11 @@ public: GL(glViewport(0, 0, screen_width_, screen_height_)); std::pair target_ratio = None(); - if(stretch_ == fill) + if(config_.stretch == fill) target_ratio = Fill(); - else if(stretch_ == uniform) + else if(config_.stretch == uniform) target_ratio = Uniform(); - else if(stretch_ == uniform_to_fill) + else if(config_.stretch == uniform_to_fill) target_ratio = UniformToFill(); width_ = target_ratio.first; @@ -234,94 +421,37 @@ 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); - - auto ptr = glMapBuffer(GL_PIXEL_UNPACK_BUFFER, GL_WRITE_ONLY); - if(ptr) - { - fast_memcpy(reinterpret_cast(ptr), frame->image_data().begin(), frame->image_data().size()); - glUnmapBuffer(GL_PIXEL_UNPACK_BUFFER); // release the mapped buffer - } - - 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); - - std::rotate(pbos_.begin(), pbos_.begin() + 1, pbos_.end()); - } - - void send(const safe_ptr& frame) - { - if(executor_.size() >= executor_.capacity()-1) - return; - - executor_.begin_invoke([=] - { - perf_timer_.restart(); - 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_.fps*0.5)); - }); - } - - std::wstring print() const - { - return L"ogl[" + boost::lexical_cast(screen_index_) + L"|" + format_desc_.name + L"]"; - } - - size_t buffer_depth() const{return 2;} }; struct ogl_consumer_proxy : public core::frame_consumer { - size_t screen_index_; - caspar::stretch stretch_; - bool windowed_; - bool key_only_; - + const configuration config_; 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){} + ogl_consumer_proxy(const configuration& config) + : config_(config){} virtual void initialize(const core::video_format_desc& format_desc) { - consumer_.reset(new ogl_consumer(screen_index_, stretch_, windowed_, format_desc)); + consumer_.reset(new ogl_consumer(config_, format_desc)); } - virtual void send(const safe_ptr& frame) + virtual bool send(const safe_ptr& frame) { - consumer_->send(frame); + try + { + consumer_->send(frame); + } + catch(...) + { + CASPAR_LOG_CURRENT_EXCEPTION() + return false; + } + + return true; } virtual std::wstring print() const @@ -329,47 +459,49 @@ public: return consumer_->print(); } - virtual bool key_only() const + virtual bool has_synchronization_clock() const + { + return false; + } + + virtual const core::video_format_desc& get_video_format_desc() const { - return key_only_; + 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(); - size_t screen_index = 0; - stretch stretch = stretch::fill; - bool windowed = true; - + configuration config; + if(params.size() > 1) - screen_index = lexical_cast_or_default(params[2], screen_index); + config.screen_index = lexical_cast_or_default(params[2], config.screen_index); if(params.size() > 2) - windowed = lexical_cast_or_default(params[3], windowed); + config.windowed = lexical_cast_or_default(params[3], config.windowed); - bool key_only = std::find(params.begin(), params.end(), L"KEY_ONLY") != params.end(); + config.key_only = std::find(params.begin(), params.end(), L"KEY_ONLY") != params.end(); - return make_safe(screen_index, stretch, windowed, key_only); + return make_safe(config); } 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); + configuration config; + config.screen_index = ptree.get("device", config.screen_index); + config.windowed = ptree.get("windowed", config.windowed); + config.key_only = ptree.get("key-only", config.key_only ); - 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); + auto stretch_str = ptree.get("stretch", "default"); + if(stretch_str == "uniform") + config.stretch = stretch::uniform; + else if(stretch_str == "uniform_to_fill") + config.stretch = stretch::uniform_to_fill; + + return make_safe(config); } } \ No newline at end of file