/*\r
-* copyright (c) 2010 Sveriges Television AB <info@casparcg.com>\r
+* Copyright 2013 Sveriges Television AB http://casparcg.com/\r
*\r
-* This file is part of CasparCG.\r
+* This file is part of CasparCG (www.casparcg.com).\r
*\r
-* CasparCG is free software: you can redistribute it and/or modify\r
-* it under the terms of the GNU General Public License as published by\r
-* the Free Software Foundation, either version 3 of the License, or\r
-* (at your option) any later version.\r
+* CasparCG is free software: you can redistribute it and/or modify\r
+* it under the terms of the GNU General Public License as published by\r
+* the Free Software Foundation, either version 3 of the License, or\r
+* (at your option) any later version.\r
*\r
-* CasparCG is distributed in the hope that it will be useful,\r
-* but WITHOUT ANY WARRANTY; without even the implied warranty of\r
-* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
-* GNU General Public License for more details.\r
-\r
-* You should have received a copy of the GNU General Public License\r
-* along with CasparCG. If not, see <http://www.gnu.org/licenses/>.\r
+* CasparCG is distributed in the hope that it will be useful,\r
+* but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
+* GNU General Public License for more details.\r
+*\r
+* You should have received a copy of the GNU General Public License\r
+* along with CasparCG. If not, see <http://www.gnu.org/licenses/>.\r
*\r
+* Author: Robert Nagy, ronag89@gmail.com\r
*/\r
\r
-#define NOMINMAX\r
-\r
-#include <windows.h>\r
-#include <Glee.h>\r
-#include <SFML/Window.hpp>\r
-\r
#include "ogl_consumer.h"\r
\r
-#include <core/video_format.h>\r
-#include <core/consumer/frame/read_frame.h>\r
+#include <GL/glew.h>\r
+#include <SFML/Window.hpp>\r
\r
+#include <common/diagnostics/graph.h>\r
#include <common/gl/gl_check.h>\r
-#include <common/concurrency/executor.h>\r
+#include <common/log/log.h>\r
#include <common/memory/safe_ptr.h>\r
#include <common/memory/memcpy.h>\r
-#include <common/diagnostics/graph.h>\r
+#include <common/memory/memshfl.h>\r
#include <common/utility/timer.h>\r
+#include <common/utility/string.h>\r
+#include <common/concurrency/future_util.h>\r
+#include <common/concurrency/executor.h>\r
+#include <common/exception/win32_exception.h>\r
+\r
+#include <ffmpeg/producer/filter/filter.h>\r
+\r
+#include <core/parameters/parameters.h>\r
+#include <core/video_format.h>\r
+#include <core/mixer/read_frame.h>\r
+#include <core/consumer/frame_consumer.h>\r
\r
-#include <boost/thread.hpp>\r
#include <boost/timer.hpp>\r
+#include <boost/circular_buffer.hpp>\r
+#include <boost/foreach.hpp>\r
+#include <boost/thread.hpp>\r
+#include <boost/property_tree/ptree.hpp>\r
+\r
+#include <tbb/atomic.h>\r
+#include <tbb/concurrent_queue.h>\r
+\r
+#include <boost/assign.hpp>\r
\r
#include <algorithm>\r
-#include <array>\r
+#include <vector>\r
\r
-namespace caspar {\r
+#if defined(_MSC_VER)\r
+#pragma warning (push)\r
+#pragma warning (disable : 4244)\r
+#endif\r
+extern "C" \r
+{\r
+ #define __STDC_CONSTANT_MACROS\r
+ #define __STDC_LIMIT_MACROS\r
+ #include <libavcodec/avcodec.h>\r
+ #include <libavutil/imgutils.h>\r
+}\r
+#if defined(_MSC_VER)\r
+#pragma warning (pop)\r
+#endif\r
\r
-struct ogl_consumer::implementation : boost::noncopyable\r
-{ \r
- boost::unique_future<void> active_;\r
+typedef int (*PFNWGLEXTGETSWAPINTERVALPROC) (void);\r
+ \r
+namespace caspar { namespace ogl {\r
\r
- float width_;\r
- float height_;\r
+enum stretch\r
+{\r
+ none,\r
+ uniform,\r
+ fill,\r
+ uniform_to_fill\r
+};\r
\r
- GLuint texture_;\r
- std::array<GLuint, 2> pbos_;\r
+struct configuration\r
+{\r
+ enum aspect_ratio\r
+ {\r
+ aspect_4_3 = 0,\r
+ aspect_16_9,\r
+ aspect_invalid,\r
+ };\r
+ \r
+ std::wstring name;\r
+ size_t screen_index;\r
+ stretch stretch;\r
+ bool windowed;\r
+ bool auto_deinterlace;\r
+ bool key_only;\r
+ aspect_ratio aspect; \r
+ bool vsync;\r
+ bool borderless;\r
+\r
+ configuration()\r
+ : name(L"Screen consumer")\r
+ , screen_index(0)\r
+ , stretch(fill)\r
+ , windowed(true)\r
+ , auto_deinterlace(true)\r
+ , key_only(false)\r
+ , aspect(aspect_invalid)\r
+ , vsync(false)\r
+ , borderless(false)\r
+ {\r
+ }\r
+};\r
\r
- const bool windowed_;\r
- unsigned int screen_width_;\r
- unsigned int screen_height_;\r
- const unsigned int screen_index_;\r
- \r
- const stretch stretch_;\r
+struct ogl_consumer : boost::noncopyable\r
+{ \r
+ const configuration config_;\r
core::video_format_desc format_desc_;\r
+ int channel_index_;\r
+\r
+ GLuint texture_;\r
+ std::vector<GLuint> pbos_;\r
+ \r
+ float width_;\r
+ float height_; \r
+ unsigned int screen_x_;\r
+ unsigned int screen_y_;\r
+ unsigned int screen_width_;\r
+ unsigned int screen_height_;\r
+ size_t square_width_;\r
+ size_t square_height_; \r
\r
- sf::Window window_;\r
+ sf::Window window_;\r
+\r
+ std::int64_t pts_;\r
\r
- safe_ptr<diagnostics::graph> graph_;\r
- boost::timer perf_timer_;\r
+ safe_ptr<diagnostics::graph> graph_;\r
+ boost::timer perf_timer_;\r
+ boost::timer tick_timer_;\r
+\r
+ caspar::high_prec_timer wait_timer_;\r
\r
- size_t square_width_;\r
- size_t square_height_;\r
+ tbb::concurrent_bounded_queue<safe_ptr<core::read_frame>> frame_buffer_;\r
\r
- executor executor_;\r
+ boost::thread thread_;\r
+ tbb::atomic<bool> is_running_;\r
+ tbb::atomic<int64_t> current_presentation_age_;\r
+ \r
+ ffmpeg::filter filter_;\r
public:\r
- implementation(unsigned int screen_index, stretch stretch, bool windowed) \r
- : stretch_(stretch)\r
- , windowed_(windowed)\r
+ ogl_consumer(const configuration& config, const core::video_format_desc& format_desc, int channel_index) \r
+ : config_(config)\r
+ , format_desc_(format_desc)\r
+ , channel_index_(channel_index)\r
, texture_(0)\r
- , screen_index_(screen_index)\r
- , graph_(diagnostics::create_graph(narrow(print())))\r
- , executor_(print())\r
+ , pbos_(2, 0) \r
+ , screen_width_(format_desc.width)\r
+ , screen_height_(format_desc.height)\r
+ , square_width_(format_desc.square_width)\r
+ , square_height_(format_desc.square_height)\r
+ , pts_(0)\r
+ , filter_([&]() -> ffmpeg::filter\r
+ { \r
+ const auto sample_aspect_ratio = \r
+ boost::rational<int>(\r
+ format_desc.square_width, \r
+ format_desc.square_height) /\r
+ boost::rational<int>(\r
+ format_desc.width, \r
+ format_desc.height);\r
+\r
+ return ffmpeg::filter(\r
+ format_desc.width,\r
+ format_desc.height,\r
+ boost::rational<int>(format_desc.duration, format_desc.time_scale),\r
+ boost::rational<int>(format_desc.time_scale, format_desc.duration),\r
+ sample_aspect_ratio,\r
+ AV_PIX_FMT_BGRA,\r
+ boost::assign::list_of(AV_PIX_FMT_BGRA),\r
+ format_desc.field_mode == core::field_mode::progressive || !config.auto_deinterlace ? "" : "format=pix_fmts=gbrp,YADIF=1:-1");\r
+ }())\r
{ \r
- executor_.set_capacity(3);\r
- graph_->add_guide("frame-time", 0.5);\r
- graph_->set_color("frame-time", diagnostics::color(1.0f, 0.0f, 0.0f));\r
- }\r
+ if(format_desc_.format == core::video_format::ntsc && config_.aspect == configuration::aspect_4_3)\r
+ {\r
+ // Use default values which are 4:3.\r
+ }\r
+ else\r
+ {\r
+ if(config_.aspect == configuration::aspect_16_9)\r
+ square_width_ = (format_desc.height*16)/9;\r
+ else if(config_.aspect == configuration::aspect_4_3)\r
+ square_width_ = (format_desc.height*4)/3;\r
+ }\r
\r
- ~implementation()\r
+ frame_buffer_.set_capacity(2);\r
+ \r
+ graph_->set_color("tick-time", diagnostics::color(0.0f, 0.6f, 0.9f)); \r
+ graph_->set_color("frame-time", diagnostics::color(0.1f, 1.0f, 0.1f));\r
+ graph_->set_color("dropped-frame", diagnostics::color(0.3f, 0.6f, 0.3f));\r
+\r
+ graph_->set_text(print());\r
+ diagnostics::register_graph(graph_);\r
+ \r
+ DISPLAY_DEVICE d_device = {sizeof(d_device), 0}; \r
+ std::vector<DISPLAY_DEVICE> displayDevices;\r
+ for(int n = 0; EnumDisplayDevices(NULL, n, &d_device, NULL); ++n)\r
+ displayDevices.push_back(d_device);\r
+\r
+ if(config_.screen_index >= displayDevices.size())\r
+ CASPAR_LOG(warning) << print() << L" Invalid screen-index: " << config_.screen_index;\r
+ \r
+ DEVMODE devmode = {};\r
+ if(!EnumDisplaySettings(displayDevices[config_.screen_index].DeviceName, ENUM_CURRENT_SETTINGS, &devmode))\r
+ CASPAR_LOG(warning) << print() << L" Could not find display settings for screen-index: " << config_.screen_index;\r
+ \r
+ screen_x_ = devmode.dmPosition.x;\r
+ screen_y_ = devmode.dmPosition.y;\r
+ screen_width_ = config_.windowed ? square_width_ : devmode.dmPelsWidth;\r
+ screen_height_ = config_.windowed ? square_height_ : devmode.dmPelsHeight;\r
+\r
+ is_running_ = true;\r
+ current_presentation_age_ = 0;\r
+ thread_ = boost::thread([this]{run();});\r
+ }\r
+ \r
+ ~ogl_consumer()\r
{\r
- CASPAR_LOG(info) << print() << L" Shutting down."; \r
+ is_running_ = false;\r
+ frame_buffer_.try_push(make_safe<core::read_frame>());\r
+ thread_.join();\r
}\r
\r
- void initialize(const core::video_format_desc& format_desc)\r
+ void init()\r
{\r
- if(!GLEE_VERSION_2_1)\r
+ if(!GLEW_VERSION_2_1)\r
BOOST_THROW_EXCEPTION(not_supported() << msg_info("Missing OpenGL 2.1 support."));\r
- \r
- format_desc_ = format_desc;\r
\r
- square_width_ = format_desc_.width;\r
- square_height_ = format_desc_.height;\r
- \r
- if(format_desc_.format == core::video_format::pal)\r
+ window_.Create(sf::VideoMode(screen_width_, screen_height_, 32), narrow(print()), config_.borderless ? sf::Style::None : (config_.windowed ? sf::Style::Resize | sf::Style::Close : sf::Style::Fullscreen));\r
+ window_.ShowMouseCursor(false);\r
+ window_.SetPosition(screen_x_, screen_y_);\r
+ window_.SetSize(screen_width_, screen_height_);\r
+ window_.SetActive();\r
+ GL(glEnable(GL_TEXTURE_2D));\r
+ GL(glDisable(GL_DEPTH_TEST)); \r
+ GL(glClearColor(0.0, 0.0, 0.0, 0.0));\r
+ GL(glViewport(0, 0, format_desc_.width, format_desc_.height));\r
+ GL(glLoadIdentity());\r
+ \r
+ calculate_aspect();\r
+ \r
+ GL(glGenTextures(1, &texture_));\r
+ GL(glBindTexture(GL_TEXTURE_2D, texture_));\r
+ GL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR));\r
+ GL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR));\r
+ GL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP));\r
+ GL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP));\r
+ GL(glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, format_desc_.width, format_desc_.height, 0, GL_BGRA, GL_UNSIGNED_BYTE, 0));\r
+ GL(glBindTexture(GL_TEXTURE_2D, 0));\r
+ \r
+ GL(glGenBuffers(2, pbos_.data()));\r
+ \r
+ glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, pbos_[0]);\r
+ glBufferDataARB(GL_PIXEL_UNPACK_BUFFER_ARB, format_desc_.size, 0, GL_STREAM_DRAW_ARB);\r
+ glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, pbos_[1]);\r
+ glBufferDataARB(GL_PIXEL_UNPACK_BUFFER_ARB, format_desc_.size, 0, GL_STREAM_DRAW_ARB);\r
+ glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0);\r
+ \r
+ if(config_.vsync)\r
{\r
- square_width_ = 768;\r
- square_height_ = 576;\r
+ auto wglSwapIntervalEXT = reinterpret_cast<void(APIENTRY*)(int)>(wglGetProcAddress("wglSwapIntervalEXT"));\r
+ if(wglSwapIntervalEXT)\r
+ {\r
+ wglSwapIntervalEXT(1);\r
+ CASPAR_LOG(info) << print() << " Successfully enabled vsync.";\r
+ }\r
+ else\r
+ CASPAR_LOG(info) << print() << " Failed to enable vsync.";\r
}\r
- else if(format_desc_.format == core::video_format::ntsc)\r
+\r
+ CASPAR_LOG(info) << print() << " Successfully Initialized.";\r
+ }\r
+\r
+ void uninit()\r
+ { \r
+ if(texture_)\r
+ glDeleteTextures(1, &texture_);\r
+\r
+ BOOST_FOREACH(auto& pbo, pbos_)\r
{\r
- square_width_ = 720;\r
- square_height_ = 547;\r
+ if(pbo)\r
+ glDeleteBuffers(1, &pbo);\r
}\r
+ }\r
\r
- screen_width_ = format_desc.width;\r
- screen_height_ = format_desc.height;\r
-#ifdef _WIN32\r
- DISPLAY_DEVICE d_device; \r
- memset(&d_device, 0, sizeof(d_device));\r
- d_device.cb = sizeof(d_device);\r
+ void run()\r
+ {\r
+ win32_exception::ensure_handler_installed_for_thread(\r
+ "ogl-consumer-thread");\r
\r
- std::vector<DISPLAY_DEVICE> displayDevices;\r
- for(int n = 0; EnumDisplayDevices(NULL, n, &d_device, NULL); ++n)\r
+ try\r
{\r
- displayDevices.push_back(d_device);\r
- memset(&d_device, 0, sizeof(d_device));\r
- d_device.cb = sizeof(d_device);\r
+ init();\r
+\r
+ while(is_running_)\r
+ { \r
+ try\r
+ {\r
+\r
+ sf::Event e; \r
+ while(window_.GetEvent(e))\r
+ {\r
+ if(e.Type == sf::Event::Resized)\r
+ calculate_aspect();\r
+ else if(e.Type == sf::Event::Closed)\r
+ is_running_ = false;\r
+ }\r
+ \r
+ safe_ptr<core::read_frame> frame;\r
+\r
+ frame_buffer_.pop(frame);\r
+ \r
+ if(static_cast<size_t>(frame->image_data().size()) != format_desc_.size)\r
+ continue;\r
+ \r
+ {\r
+ auto av_frame = safe_ptr<AVFrame>(av_frame_alloc(), [frame](AVFrame* frame)\r
+ {\r
+ av_frame_free(&frame);\r
+ });\r
+ \r
+ av_frame->linesize[0] = format_desc_.width*4; \r
+ av_frame->format = PIX_FMT_BGRA;\r
+ av_frame->width = format_desc_.width;\r
+ av_frame->height = format_desc_.height;\r
+ av_frame->interlaced_frame = format_desc_.field_mode != core::field_mode::progressive;\r
+ av_frame->top_field_first = format_desc_.field_mode == core::field_mode::upper ? 1 : 0;\r
+ av_frame->data[0] = const_cast<uint8_t*>(frame->image_data().begin());\r
+ av_frame->pts = pts_++;\r
+ filter_.push(av_frame);\r
+ }\r
+\r
+ while(true)\r
+ {\r
+ perf_timer_.restart();\r
+ auto av_frame = filter_.poll();\r
+\r
+ if (!av_frame)\r
+ break;\r
+ \r
+ render(make_safe_ptr(av_frame), frame->image_data().size());\r
+ graph_->set_value("frame-time", perf_timer_.elapsed() * format_desc_.fps * 0.5);\r
+\r
+ wait_for_vblank_and_display(); // progressive fram\r
+ }\r
+ \r
+ current_presentation_age_ = frame->get_age_millis();\r
+ \r
+ graph_->set_value("tick-time", tick_timer_.elapsed()*format_desc_.fps*0.5); \r
+ tick_timer_.restart();\r
+ }\r
+ catch(...)\r
+ {\r
+ CASPAR_LOG_CURRENT_EXCEPTION();\r
+ is_running_ = false;\r
+ }\r
+ }\r
+\r
+ uninit();\r
+ }\r
+ catch(...)\r
+ {\r
+ CASPAR_LOG_CURRENT_EXCEPTION();\r
}\r
+ }\r
\r
- if(screen_index_ >= displayDevices.size())\r
- BOOST_THROW_EXCEPTION(out_of_range() << arg_name_info("screen_index_") << msg_info(narrow(print())));\r
- \r
- DEVMODE devmode;\r
- memset(&devmode, 0, sizeof(devmode));\r
- \r
- if(!EnumDisplaySettings(displayDevices[screen_index_].DeviceName, ENUM_CURRENT_SETTINGS, &devmode))\r
- BOOST_THROW_EXCEPTION(invalid_operation() << arg_name_info("screen_index") << msg_info(narrow(print()) + " EnumDisplaySettings"));\r
- \r
- screen_width_ = windowed_ ? square_width_ : devmode.dmPelsWidth;\r
- screen_height_ = windowed_ ? square_height_ : devmode.dmPelsHeight;\r
-#else\r
- if(!windowed)\r
- BOOST_THROW_EXCEPTION(not_supported() << msg_info(narrow(print() + " doesn't support non-Win32 fullscreen"));\r
-\r
- if(screen_index != 0)\r
- CASPAR_LOG(warning) << print() << " only supports screen_index=0 for non-Win32";\r
-#endif \r
- executor_.start();\r
- executor_.invoke([=]\r
+ void try_sleep_almost_until_vblank()\r
+ {\r
+ static const double THRESHOLD = 0.003;\r
+ double threshold = config_.vsync ? THRESHOLD : 0.0;\r
+\r
+ auto frame_time = 1.0 / (format_desc_.fps * format_desc_.field_count);\r
+\r
+ wait_timer_.tick(frame_time - threshold);\r
+ }\r
+\r
+ void wait_for_vblank_and_display()\r
+ {\r
+ try_sleep_almost_until_vblank();\r
+ window_.Display();\r
+ // Make sure that the next tick measures the duration from this point in time.\r
+ wait_timer_.tick(0.0);\r
+ }\r
+\r
+ void render(safe_ptr<AVFrame> av_frame, int image_data_size)\r
+ {\r
+ glBindTexture(GL_TEXTURE_2D, texture_);\r
+\r
+ glBindBuffer(GL_PIXEL_UNPACK_BUFFER, pbos_[0]);\r
+ glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, format_desc_.width, format_desc_.height, GL_BGRA, GL_UNSIGNED_BYTE, 0);\r
+\r
+ glBindBuffer(GL_PIXEL_UNPACK_BUFFER, pbos_[1]);\r
+ glBufferData(GL_PIXEL_UNPACK_BUFFER, format_desc_.size, 0, GL_STREAM_DRAW);\r
+\r
+\r
+ auto ptr = glMapBuffer(GL_PIXEL_UNPACK_BUFFER, GL_WRITE_ONLY);\r
+ if(ptr)\r
{\r
- window_.Create(sf::VideoMode(screen_width_, screen_height_, 32), narrow(print()), windowed_ ? sf::Style::Resize : sf::Style::Fullscreen);\r
- window_.ShowMouseCursor(false);\r
- window_.SetPosition(devmode.dmPosition.x, devmode.dmPosition.y);\r
- window_.SetSize(screen_width_, screen_height_);\r
- window_.SetActive();\r
- GL(glEnable(GL_TEXTURE_2D));\r
- GL(glDisable(GL_DEPTH_TEST)); \r
- GL(glClearColor(0.0, 0.0, 0.0, 0.0));\r
- GL(glViewport(0, 0, format_desc_.width, format_desc_.height));\r
- glLoadIdentity();\r
+ if(config_.key_only)\r
+ { \r
+ tbb::parallel_for(0, av_frame->height, 1, [&](int y)\r
+ {\r
+ fast_memshfl(reinterpret_cast<char*>(ptr) + y * format_desc_.width * 4, av_frame->data[0] + y * av_frame->linesize[0], format_desc_.width * 4, 0x0F0F0F0F, 0x0B0B0B0B, 0x07070707, 0x03030303);\r
+ });\r
+ }\r
+ else\r
+ {\r
+ tbb::parallel_for(0, av_frame->height, 1, [&](int y)\r
+ {\r
+ fast_memcpy(reinterpret_cast<char*>(ptr) + y * format_desc_.width * 4, av_frame->data[0] + y * av_frame->linesize[0], format_desc_.width * 4);\r
+ });\r
+ }\r
+\r
+ glUnmapBuffer(GL_PIXEL_UNPACK_BUFFER); // release the mapped buffer\r
+ }\r
+\r
+ glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);\r
\r
- calculate_aspect();\r
- \r
- glGenTextures(1, &texture_);\r
- glBindTexture(GL_TEXTURE_2D, texture_);\r
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);\r
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);\r
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);\r
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);\r
- glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, format_desc_.width, format_desc_.height, 0, GL_BGRA, GL_UNSIGNED_BYTE, 0);\r
- glBindTexture(GL_TEXTURE_2D, 0);\r
- \r
- GL(glGenBuffers(2, pbos_.data()));\r
- \r
- glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, pbos_[0]);\r
- glBufferDataARB(GL_PIXEL_UNPACK_BUFFER_ARB, format_desc_.size, 0, GL_STREAM_DRAW_ARB);\r
- glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, pbos_[1]);\r
- glBufferDataARB(GL_PIXEL_UNPACK_BUFFER_ARB, format_desc_.size, 0, GL_STREAM_DRAW_ARB);\r
- glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0);\r
- });\r
- CASPAR_LOG(info) << print() << " Sucessfully initialized.";\r
+ GL(glClear(GL_COLOR_BUFFER_BIT)); \r
+ glBegin(GL_QUADS);\r
+ glTexCoord2f(0.0f, 1.0f); glVertex2f(-width_, -height_);\r
+ glTexCoord2f(1.0f, 1.0f); glVertex2f( width_, -height_);\r
+ glTexCoord2f(1.0f, 0.0f); glVertex2f( width_, height_);\r
+ glTexCoord2f(0.0f, 0.0f); glVertex2f(-width_, height_);\r
+ glEnd();\r
+ \r
+ glBindTexture(GL_TEXTURE_2D, 0);\r
+\r
+ std::rotate(pbos_.begin(), pbos_.begin() + 1, pbos_.end());\r
+ }\r
+\r
+ boost::unique_future<bool> send(const safe_ptr<core::read_frame>& frame)\r
+ {\r
+ if (!frame_buffer_.try_push(frame))\r
+ graph_->set_tag("dropped-frame"); \r
+\r
+ return wrap_as_future(is_running_.load());\r
}\r
\r
+ std::wstring channel_and_format() const\r
+ {\r
+ return L"[" + boost::lexical_cast<std::wstring>(channel_index_) + L"|" + format_desc_.name + L"]";\r
+ }\r
+ \r
+ std::wstring print() const\r
+ { \r
+ return config_.name + L" " + channel_and_format();\r
+ }\r
+ \r
void calculate_aspect()\r
{\r
- if(windowed_)\r
+ if(config_.windowed)\r
{\r
screen_height_ = window_.GetHeight();\r
screen_width_ = window_.GetWidth();\r
GL(glViewport(0, 0, screen_width_, screen_height_));\r
\r
std::pair<float, float> target_ratio = None();\r
- if(stretch_ == fill)\r
+ if(config_.stretch == fill)\r
target_ratio = Fill();\r
- else if(stretch_ == uniform)\r
+ else if(config_.stretch == uniform)\r
target_ratio = Uniform();\r
- else if(stretch_ == uniform_to_fill)\r
+ else if(config_.stretch == uniform_to_fill)\r
target_ratio = UniformToFill();\r
\r
width_ = target_ratio.first;\r
\r
return std::make_pair(width, height);\r
}\r
+};\r
\r
- void render(const safe_ptr<const core::read_frame>& frame)\r
- { \r
- glBindTexture(GL_TEXTURE_2D, texture_);\r
- glBindBuffer(GL_PIXEL_UNPACK_BUFFER, pbos_[0]);\r
-\r
- glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, format_desc_.width, format_desc_.height, GL_BGRA, GL_UNSIGNED_BYTE, 0);\r
\r
- glBindBuffer(GL_PIXEL_UNPACK_BUFFER, pbos_[1]);\r
+struct ogl_consumer_proxy : public core::frame_consumer\r
+{\r
+ const configuration config_;\r
+ std::unique_ptr<ogl_consumer> consumer_;\r
\r
- glBufferData(GL_PIXEL_UNPACK_BUFFER, format_desc_.size, 0, GL_STREAM_DRAW);\r
+public:\r
\r
- auto ptr = glMapBuffer(GL_PIXEL_UNPACK_BUFFER, GL_WRITE_ONLY);\r
- if(ptr)\r
+ ogl_consumer_proxy(const configuration& config)\r
+ : config_(config){}\r
+ \r
+ ~ogl_consumer_proxy()\r
+ {\r
+ if(consumer_)\r
{\r
- fast_memcpy(reinterpret_cast<char*>(ptr), frame->image_data().begin(), frame->image_data().size());\r
- glUnmapBuffer(GL_PIXEL_UNPACK_BUFFER); // release the mapped buffer\r
+ auto str = print();\r
+ consumer_.reset();\r
+ CASPAR_LOG(info) << str << L" Successfully Uninitialized."; \r
}\r
+ }\r
\r
- glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);\r
- \r
- GL(glClear(GL_COLOR_BUFFER_BIT)); \r
- glBegin(GL_QUADS);\r
- glTexCoord2f(0.0f, 1.0f); glVertex2f(-width_, -height_);\r
- glTexCoord2f(1.0f, 1.0f); glVertex2f( width_, -height_);\r
- glTexCoord2f(1.0f, 0.0f); glVertex2f( width_, height_);\r
- glTexCoord2f(0.0f, 0.0f); glVertex2f(-width_, height_);\r
- glEnd();\r
- \r
- glBindTexture(GL_TEXTURE_2D, 0);\r
+ // frame_consumer\r
\r
- std::rotate(pbos_.begin(), pbos_.begin() + 1, pbos_.end());\r
+ virtual void initialize(const core::video_format_desc& format_desc, int channel_index) override\r
+ {\r
+ consumer_.reset();\r
+ consumer_.reset(new ogl_consumer(config_, format_desc, channel_index));\r
+ CASPAR_LOG(info) << print() << L" Successfully Initialized."; \r
}\r
- \r
- void send(const safe_ptr<const core::read_frame>& frame)\r
+\r
+ virtual int64_t presentation_frame_age_millis() const override\r
{\r
- if(executor_.size() >= executor_.capacity()-1)\r
- return;\r
+ return consumer_ ? consumer_->current_presentation_age_ : 0;\r
+ }\r
\r
- executor_.begin_invoke([=]\r
- {\r
- perf_timer_.restart();\r
- sf::Event e;\r
- while(window_.GetEvent(e))\r
- {\r
- if(e.Type == sf::Event::Resized)\r
- calculate_aspect();\r
- }\r
- render(frame);\r
- window_.Display();\r
- graph_->update_value("frame-time", static_cast<float>(perf_timer_.elapsed()*format_desc_.fps*0.5));\r
- });\r
+ virtual boost::unique_future<bool> send(const safe_ptr<core::read_frame>& frame) override\r
+ {\r
+ return consumer_->send(frame);\r
+ }\r
+ \r
+ virtual std::wstring print() const override\r
+ {\r
+ return consumer_ ? consumer_->print() : L"[ogl_consumer]";\r
}\r
\r
- std::wstring print() const\r
+ virtual boost::property_tree::wptree info() const override\r
{\r
- return L"ogl[" + boost::lexical_cast<std::wstring>(screen_index_) + L"]";\r
+ boost::property_tree::wptree info;\r
+ info.add(L"type", L"ogl-consumer");\r
+ info.add(L"key-only", config_.key_only);\r
+ info.add(L"windowed", config_.windowed);\r
+ info.add(L"auto-deinterlace", config_.auto_deinterlace);\r
+ return info;\r
}\r
\r
- size_t buffer_depth() const{return 2;}\r
-};\r
+ virtual bool has_synchronization_clock() const override\r
+ {\r
+ return false;\r
+ }\r
+ \r
+ virtual size_t buffer_depth() const override\r
+ {\r
+ return 1;\r
+ }\r
\r
-ogl_consumer::ogl_consumer(ogl_consumer&& other) : impl_(std::move(other.impl_)){}\r
-ogl_consumer::ogl_consumer(unsigned int screen_index, stretch stretch, bool windowed) : impl_(new implementation(screen_index, stretch, windowed)){}\r
-void ogl_consumer::send(const safe_ptr<const core::read_frame>& frame){impl_->send(frame);}\r
-size_t ogl_consumer::buffer_depth() const{return impl_->buffer_depth();}\r
-void ogl_consumer::initialize(const core::video_format_desc& format_desc)\r
-{\r
- // TODO: Ugly\r
- if(impl_->executor_.is_running())\r
- impl_.reset(new implementation(impl_->screen_index_, impl_->stretch_, impl_->windowed_));\r
- impl_->initialize(format_desc);\r
-}\r
-std::wstring ogl_consumer::print() const {return impl_->print();}\r
+ virtual int index() const override\r
+ {\r
+ return 600 + config_.screen_index;\r
+ }\r
+}; \r
\r
-safe_ptr<core::frame_consumer> create_ogl_consumer(const std::vector<std::wstring>& params)\r
+safe_ptr<core::frame_consumer> create_consumer(const core::parameters& params)\r
{\r
- if(params.size() < 1 || params[0] != L"OGL")\r
+ if(params.size() < 1 || params[0] != L"SCREEN")\r
return core::frame_consumer::empty();\r
\r
- unsigned int screen_index = 0;\r
- stretch stretch = stretch::fill;\r
- bool windowed = true;\r
- \r
- if(params.size() > 1) \r
- screen_index = lexical_cast_or_default<int>(params[2], screen_index);\r
+ configuration config;\r
+ \r
+ if(params.size() > 1)\r
+ config.screen_index =\r
+ lexical_cast_or_default<int>(params[1], config.screen_index);\r
+\r
+ config.screen_index = params.get(L"DEVICE", config.screen_index);\r
+ config.windowed = !params.has(L"FULLSCREEN");\r
+ config.key_only = params.has(L"KEY_ONLY");\r
+ config.name = params.get(L"NAME", config.name);\r
+ config.borderless = params.has(L"BORDERLESS");\r
\r
- if(params.size() > 2) \r
- windowed = lexical_cast_or_default<bool>(params[3], windowed);\r
+ return make_safe<ogl_consumer_proxy>(config);\r
+}\r
\r
- return make_safe<ogl_consumer>(screen_index, stretch, windowed);\r
+safe_ptr<core::frame_consumer> create_consumer(const boost::property_tree::wptree& ptree) \r
+{\r
+ configuration config;\r
+ config.name = ptree.get(L"name", config.name);\r
+ config.screen_index = ptree.get(L"device", config.screen_index+1)-1;\r
+ config.windowed = ptree.get(L"windowed", config.windowed);\r
+ config.key_only = ptree.get(L"key-only", config.key_only);\r
+ config.auto_deinterlace = ptree.get(L"auto-deinterlace", config.auto_deinterlace);\r
+ config.vsync = ptree.get(L"vsync", config.vsync);\r
+ config.borderless = ptree.get(L"borderless", config.borderless);\r
+\r
+ auto stretch_str = ptree.get(L"stretch", L"default");\r
+ if(stretch_str == L"uniform")\r
+ config.stretch = stretch::uniform;\r
+ else if(stretch_str == L"uniform_to_fill")\r
+ config.stretch = stretch::uniform_to_fill;\r
+\r
+ auto aspect_str = ptree.get(L"aspect-ratio", L"default");\r
+ if(aspect_str == L"16:9")\r
+ config.aspect = configuration::aspect_16_9;\r
+ else if(aspect_str == L"4:3")\r
+ config.aspect = configuration::aspect_4_3;\r
+ \r
+ return make_safe<ogl_consumer_proxy>(config);\r
}\r
\r
-}
\ No newline at end of file
+}}
\ No newline at end of file