*\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/mixer/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
+\r
+#include <ffmpeg/producer/filter/filter.h>\r
+\r
+#include <core/video_format.h>\r
+#include <core/mixer/read_frame.h>\r
+#include <core/consumer/frame_consumer.h>\r
\r
#include <boost/timer.hpp>\r
#include <boost/circular_buffer.hpp>\r
+#include <boost/foreach.hpp>\r
+#include <boost/thread.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
+#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
namespace caspar {\r
\r
\r
struct ogl_consumer : boost::noncopyable\r
{ \r
+ core::video_format_desc format_desc_;\r
+ \r
+ GLuint texture_;\r
+ std::vector<GLuint> pbos_;\r
+ \r
float width_;\r
float height_;\r
-\r
- GLuint texture_;\r
- std::array<GLuint, 2> pbos_;\r
+ \r
+ const stretch stretch_;\r
\r
const bool windowed_;\r
+ unsigned int screen_x_;\r
+ unsigned int screen_y_;\r
unsigned int screen_width_;\r
unsigned int screen_height_;\r
const unsigned int screen_index_;\r
- \r
- const stretch stretch_;\r
- core::video_format_desc format_desc_;\r
+\r
+ size_t square_width_;\r
+ size_t square_height_; \r
\r
sf::Window window_;\r
\r
safe_ptr<diagnostics::graph> graph_;\r
boost::timer perf_timer_;\r
\r
- size_t square_width_;\r
- size_t square_height_;\r
+ boost::circular_buffer<safe_ptr<core::read_frame>> input_buffer_;\r
+ tbb::concurrent_bounded_queue<safe_ptr<core::read_frame>> frame_buffer_;\r
\r
- boost::circular_buffer<safe_ptr<core::read_frame>> frame_buffer_;\r
+ boost::thread thread_;\r
+ tbb::atomic<bool> is_running_;\r
\r
- executor executor_;\r
+ \r
+ filter filter_;\r
+\r
+ const bool key_only_;\r
public:\r
- ogl_consumer(unsigned int screen_index, stretch stretch, bool windowed, const core::video_format_desc& format_desc) \r
- : stretch_(stretch)\r
- , windowed_(windowed)\r
+ ogl_consumer(unsigned int screen_index, stretch stretch, bool windowed, const core::video_format_desc& format_desc, bool key_only) \r
+ : format_desc_(format_desc)\r
, texture_(0)\r
- , screen_index_(screen_index)\r
- , format_desc_(format_desc_)\r
+ , pbos_(2, 0)\r
+ , stretch_(stretch)\r
+ , windowed_(windowed)\r
+ , screen_index_(screen_index) \r
+ , screen_width_(format_desc.width)\r
+ , screen_height_(format_desc.height)\r
+ , square_width_(format_desc.width)\r
+ , square_height_(format_desc.height)\r
, graph_(diagnostics::create_graph(narrow(print())))\r
- , frame_buffer_(core::consumer_buffer_depth())\r
- , executor_(print())\r
+ , input_buffer_(core::consumer_buffer_depth()-1)\r
+ , key_only_(key_only)\r
+ , filter_(format_desc.field_mode == core::field_mode::progressive ? L"" : L"YADIF=0:-1", boost::assign::list_of(PIX_FMT_BGRA))\r
{ \r
+ if(format_desc.field_mode != core::field_mode::progressive)\r
+ CASPAR_LOG(info) << print() << L" Deinterlacer enabled.";\r
+\r
+ frame_buffer_.set_capacity(2);\r
+\r
graph_->add_guide("frame-time", 0.5);\r
graph_->set_color("frame-time", diagnostics::color(1.0f, 0.0f, 0.0f));\r
- \r
- format_desc_ = format_desc;\r
-\r
- square_width_ = format_desc_.width;\r
- square_height_ = format_desc_.height;\r
- \r
+ graph_->set_color("dropped-frame", diagnostics::color(0.3f, 0.6f, 0.3f));\r
+ \r
if(format_desc_.format == core::video_format::pal)\r
{\r
- square_width_ = 768;\r
- square_height_ = 576;\r
+ square_width_ = 768;\r
+ square_height_ = 576;\r
}\r
else if(format_desc_.format == core::video_format::ntsc)\r
{\r
- square_width_ = 720;\r
- square_height_ = 547;\r
+ square_width_ = 720;\r
+ square_height_ = 547;\r
}\r
-\r
- screen_width_ = format_desc.width;\r
- screen_height_ = format_desc.height;\r
-#ifdef _WIN32\r
+ \r
DISPLAY_DEVICE d_device; \r
memset(&d_device, 0, sizeof(d_device));\r
d_device.cb = sizeof(d_device);\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_.invoke([=]\r
- {\r
- if(!GLEE_VERSION_2_1)\r
- BOOST_THROW_EXCEPTION(not_supported() << msg_info("Missing OpenGL 2.1 support."));\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
+ screen_x_ = devmode.dmPosition.x;\r
+ screen_y_ = devmode.dmPosition.y;\r
+ screen_width_ = windowed_ ? square_width_ : devmode.dmPelsWidth;\r
+ screen_height_ = windowed_ ? square_height_ : devmode.dmPelsHeight;\r
+ \r
+ is_running_ = true;\r
+ thread_ = boost::thread([this]{run();});\r
+ }\r
+ \r
+ ~ogl_consumer()\r
+ {\r
+ is_running_ = false;\r
+ frame_buffer_.try_push(make_safe<core::read_frame>());\r
+ thread_.join();\r
+ }\r
+\r
+ void init()\r
+ {\r
+ if(!GLEW_VERSION_2_1)\r
+ BOOST_THROW_EXCEPTION(not_supported() << msg_info("Missing OpenGL 2.1 support."));\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(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
+ 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
+ GL(glGenTextures(1, &texture_));\r
+ GL(glBindTexture(GL_TEXTURE_2D, texture_));\r
+ GL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST));\r
+ GL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST));\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
- 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
+ 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
}\r
+\r
+ void uninit()\r
+ { \r
+ if(texture_)\r
+ glDeleteTextures(1, &texture_);\r
+\r
+ BOOST_FOREACH(auto& pbo, pbos_)\r
+ {\r
+ if(pbo)\r
+ glDeleteBuffers(1, &pbo);\r
+ }\r
+\r
+ CASPAR_LOG(info) << print() << " Sucessfully Uninitialized.";\r
+ }\r
+\r
+ void run()\r
+ {\r
+ try\r
+ {\r
+ init();\r
+\r
+ while(is_running_)\r
+ { \r
+ try\r
+ {\r
+ perf_timer_.restart();\r
+\r
+ sf::Event e; \r
+ while(window_.GetEvent(e))\r
+ {\r
+ if(e.Type == sf::Event::Resized)\r
+ calculate_aspect();\r
+ }\r
+ \r
+ safe_ptr<core::read_frame> frame;\r
+ frame_buffer_.pop(frame);\r
+ render(frame);\r
+\r
+ window_.Display();\r
+\r
+ graph_->update_value("frame-time", static_cast<float>(perf_timer_.elapsed()*format_desc_.fps*0.5)); \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
const core::video_format_desc& get_video_format_desc() const\r
{\r
return format_desc_;\r
}\r
\r
+ safe_ptr<AVFrame> get_av_frame()\r
+ { \r
+ safe_ptr<AVFrame> av_frame(avcodec_alloc_frame(), av_free); \r
+ avcodec_get_frame_defaults(av_frame.get());\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
+\r
+ return av_frame;\r
+ }\r
+\r
+ void render(const safe_ptr<core::read_frame>& frame)\r
+ { \r
+ if(frame->image_data().empty())\r
+ return;\r
+ \r
+ auto av_frame = get_av_frame();\r
+ av_frame->data[0] = const_cast<uint8_t*>(frame->image_data().begin());\r
+\r
+ auto frames = filter_.execute(av_frame);\r
+\r
+ if(frames.empty())\r
+ return;\r
+\r
+ av_frame = frames[0];\r
+\r
+ if(av_frame->linesize[0] != static_cast<int>(format_desc_.width*4))\r
+ {\r
+ const uint8_t *src_data[4] = {0};\r
+ memcpy(const_cast<uint8_t**>(&src_data[0]), av_frame->data, 4);\r
+ const int src_linesizes[4] = {0};\r
+ memcpy(const_cast<int*>(&src_linesizes[0]), av_frame->linesize, 4);\r
+\r
+ auto av_frame2 = get_av_frame();\r
+ av_image_alloc(av_frame2->data, av_frame2->linesize, av_frame2->width, av_frame2->height, PIX_FMT_BGRA, 16);\r
+ av_image_copy(av_frame2->data, av_frame2->linesize, src_data, src_linesizes, PIX_FMT_BGRA, av_frame2->width, av_frame2->height);\r
+ av_frame = safe_ptr<AVFrame>(av_frame2.get(), [=](AVFrame*)\r
+ {\r
+ av_freep(&av_frame2->data[0]);\r
+ });\r
+ }\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
+ auto ptr = glMapBuffer(GL_PIXEL_UNPACK_BUFFER, GL_WRITE_ONLY);\r
+ if(ptr)\r
+ {\r
+ if(key_only_)\r
+ fast_memshfl(reinterpret_cast<char*>(ptr), av_frame->data[0], frame->image_data().size(), 0x0F0F0F0F, 0x0B0B0B0B, 0x07070707, 0x03030303);\r
+ else\r
+ fast_memcpy(reinterpret_cast<char*>(ptr), av_frame->data[0], frame->image_data().size());\r
+\r
+ glUnmapBuffer(GL_PIXEL_UNPACK_BUFFER); // release the mapped buffer\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
+\r
+ std::rotate(pbos_.begin(), pbos_.begin() + 1, pbos_.end());\r
+ }\r
+\r
+ void send(const safe_ptr<core::read_frame>& frame)\r
+ {\r
+ input_buffer_.push_back(frame);\r
+\r
+ if(input_buffer_.full())\r
+ {\r
+ if(!frame_buffer_.try_push(input_buffer_.front()))\r
+ graph_->add_tag("dropped-frame");\r
+ }\r
+ }\r
+ \r
+ std::wstring print() const\r
+ { \r
+ return L"ogl[" + boost::lexical_cast<std::wstring>(screen_index_) + L"|" + format_desc_.name + L"]";\r
+ }\r
+ \r
void calculate_aspect()\r
{\r
if(windowed_)\r
\r
return std::make_pair(width, height);\r
}\r
-\r
- void render(const safe_ptr<core::read_frame>& frame)\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
- auto ptr = glMapBuffer(GL_PIXEL_UNPACK_BUFFER, GL_WRITE_ONLY);\r
- if(ptr)\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
- }\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
-\r
- std::rotate(pbos_.begin(), pbos_.begin() + 1, pbos_.end());\r
- }\r
-\r
- void send(const safe_ptr<core::read_frame>& frame)\r
- {\r
- frame_buffer_.push_back(frame);\r
-\r
- if(frame_buffer_.full())\r
- do_send(frame_buffer_.front());\r
- }\r
- \r
- void do_send(const safe_ptr<core::read_frame>& frame)\r
- { \r
- executor_.try_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
- }\r
-\r
- std::wstring print() const\r
- { \r
- return L"ogl[" + boost::lexical_cast<std::wstring>(screen_index_) + L"|" + format_desc_.name + L"]";\r
- }\r
-\r
- size_t buffer_depth() const{return 2;}\r
};\r
\r
\r
\r
virtual void initialize(const core::video_format_desc& format_desc)\r
{\r
- consumer_.reset(new ogl_consumer(screen_index_, stretch_, windowed_, format_desc));\r
+ consumer_.reset(new ogl_consumer(screen_index_, stretch_, windowed_, format_desc, key_only_));\r
}\r
\r
- virtual void send(const safe_ptr<core::read_frame>& frame)\r
+ virtual bool send(const safe_ptr<core::read_frame>& frame)\r
{\r
consumer_->send(frame);\r
+ return true;\r
}\r
\r
virtual std::wstring print() const\r
return consumer_->print();\r
}\r
\r
- virtual bool key_only() const\r
- {\r
- return key_only_;\r
- }\r
-\r
virtual bool has_synchronization_clock() const \r
{\r
return false;\r