class executor\r
{\r
public:\r
+\r
+ enum priority\r
+ {\r
+ low_priority = 0,\r
+ normal_priority,\r
+ high_priority\r
+ };\r
+\r
explicit executor(const std::function<void()>& f = nullptr)\r
{\r
+ size_ = 0;\r
is_running_ = false;\r
f_ = f != nullptr ? f : [this]{run();};\r
}\r
\r
void stop() // noexcept\r
{\r
- if(is_running_.fetch_and_store(false))\r
- {\r
- execution_queue_.clear();\r
- execution_queue_.push([](){}); \r
- }\r
+ is_running_ = false; \r
thread_.join();\r
}\r
\r
void execute() // noexcept\r
{\r
- std::function<void()> func;\r
- execution_queue_.pop(func); \r
- func();\r
+ boost::unique_lock<boost::mutex> lock(mut_);\r
+ while(size_ < 1) \r
+ cond_.wait(lock);\r
+ \r
+ try_execute();\r
}\r
\r
bool try_execute() // noexcept\r
{\r
std::function<void()> func;\r
- if(execution_queue_.try_pop(func))\r
+ if(execution_queue_[high_priority].try_pop(func) || execution_queue_[normal_priority].try_pop(func) || execution_queue_[low_priority].try_pop(func))\r
+ {\r
func();\r
+ --size_;\r
+ }\r
\r
return func != nullptr;\r
}\r
-\r
- void clear() // noexcept\r
- {\r
- execution_queue_.clear();\r
- }\r
-\r
+ \r
template<typename Func>\r
- void enqueue(Func&& func) // noexcept\r
- {\r
- execution_queue_.push([=]{try{func();}catch(...){CASPAR_LOG_CURRENT_EXCEPTION();}});\r
- }\r
- \r
- template<typename Func>\r
- auto begin_invoke(Func&& func) -> boost::unique_future<decltype(func())> // noexcept\r
+ auto begin_invoke(Func&& func, priority p = normal_priority) -> boost::unique_future<decltype(func())> // noexcept\r
{ \r
typedef decltype(func()) result_type; \r
\r
}\r
catch(boost::task_already_started&){}\r
}));\r
- execution_queue_.push([=]\r
+ execution_queue_[p].push([=]\r
{\r
try\r
{\r
catch(boost::task_already_started&){}\r
catch(...){CASPAR_LOG_CURRENT_EXCEPTION();}\r
});\r
+ ++size_;\r
+ cond_.notify_one();\r
\r
return std::move(future); \r
}\r
\r
template<typename Func>\r
- auto invoke(Func&& func) -> decltype(func())\r
+ auto invoke(Func&& func, priority p = normal_priority) -> decltype(func())\r
{\r
if(boost::this_thread::get_id() == thread_.get_id()) // Avoids potential deadlock.\r
return func();\r
\r
- return begin_invoke(std::forward<Func>(func)).get();\r
+ return begin_invoke(std::forward<Func>(func), p).get();\r
}\r
\r
private:\r
execute();\r
}\r
\r
+ tbb::atomic<size_t> size_;\r
+ boost::condition_variable cond_;\r
+ boost::mutex mut_;\r
+\r
std::function<void()> f_;\r
boost::thread thread_;\r
tbb::atomic<bool> is_running_;\r
- tbb::concurrent_bounded_queue<std::function<void()>> execution_queue_;\r
+ std::array<tbb::concurrent_bounded_queue<std::function<void()>>, 3> execution_queue_;\r
};\r
\r
}
\ No newline at end of file
struct frame_buffer_object::implementation\r
{\r
public:\r
- implementation(size_t width, size_t height, GLenum mode) : mode_(mode)\r
+ implementation(size_t width, size_t height, GLenum mode) : mode_(mode), width_(width), height_(height)\r
{\r
GL(glGenTextures(1, &texture_)); \r
GL(glBindTexture(GL_TEXTURE_2D, texture_)); \r
\r
frame_buffer_object::frame_buffer_object(size_t width, size_t height, GLenum mode) : impl_(new implementation(width, height, mode)){}\r
void frame_buffer_object::bind_pixel_source() {impl_->bind_pixel_source();}\r
+size_t frame_buffer_object::width() const{return impl_->width_;}\r
+size_t frame_buffer_object::height() const{return impl_->height_;}\r
}}
\ No newline at end of file
public:\r
frame_buffer_object(size_t width, size_t height, GLenum mode = GL_COLOR_ATTACHMENT0_EXT);\r
void bind_pixel_source();\r
+ size_t width() const;\r
+ size_t height() const;\r
private:\r
struct implementation;\r
std::shared_ptr<implementation> impl_;\r
<ClInclude Include="processor\composite_frame.h" />\r
<ClInclude Include="processor\frame_processor_device.h" />\r
<ClInclude Include="processor\image_processor.h" />\r
- <ClInclude Include="processor\image_shader.h" />\r
<ClInclude Include="processor\fwd.h" />\r
<ClInclude Include="processor\draw_frame.h" />\r
<ClInclude Include="processor\read_frame.h" />\r
<PrecompiledHeaderFile Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">../StdAfx.h</PrecompiledHeaderFile>\r
<PrecompiledHeaderFile Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">../StdAfx.h</PrecompiledHeaderFile>\r
</ClCompile>\r
- <ClCompile Include="processor\image_shader.cpp">\r
- <PrecompiledHeaderFile Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">../StdAfx.h</PrecompiledHeaderFile>\r
- <PrecompiledHeaderFile Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">../StdAfx.h</PrecompiledHeaderFile>\r
- </ClCompile>\r
<ClCompile Include="processor\read_frame.cpp">\r
<PrecompiledHeaderFile Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">../StdAfx.h</PrecompiledHeaderFile>\r
<PrecompiledHeaderFile Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">../StdAfx.h</PrecompiledHeaderFile>\r
<UniqueIdentifier>{eaa43a39-9ff0-4291-b717-4d4231d8295a}</UniqueIdentifier>\r
</Filter>\r
<Filter Include="Source\channel\processor\image">\r
- <UniqueIdentifier>{30fe40ca-8bf0-4a46-a882-16d07b2b441e}</UniqueIdentifier>\r
+ <UniqueIdentifier>{fe2c2f78-7daf-4b6d-9132-eda45b4c93e1}</UniqueIdentifier>\r
</Filter>\r
<Filter Include="Source\channel\processor\audio">\r
- <UniqueIdentifier>{43ca44bb-d718-44df-9342-1913750f7e41}</UniqueIdentifier>\r
+ <UniqueIdentifier>{e3d8bd98-8cb9-4f4a-8cf0-bd455ce9138d}</UniqueIdentifier>\r
</Filter>\r
</ItemGroup>\r
<ItemGroup>\r
<ClInclude Include="processor\draw_frame.h">\r
<Filter>Source\channel\processor\frame</Filter>\r
</ClInclude>\r
- <ClInclude Include="processor\image_shader.h">\r
- <Filter>Source\channel\processor\image</Filter>\r
- </ClInclude>\r
<ClInclude Include="processor\image_processor.h">\r
<Filter>Source\channel\processor\image</Filter>\r
</ClInclude>\r
<ClCompile Include="format\pixel_format.cpp">\r
<Filter>Source\channel\format</Filter>\r
</ClCompile>\r
- <ClCompile Include="processor\image_shader.cpp">\r
- <Filter>Source\channel\processor\image</Filter>\r
- </ClCompile>\r
<ClCompile Include="processor\image_processor.cpp">\r
<Filter>Source\channel\processor\image</Filter>\r
</ClCompile>\r
#include "audio_processor.h"\r
\r
namespace caspar { namespace core {\r
+ \r
+audio_transform& audio_transform::operator*=(const audio_transform &other) \r
+{\r
+ volume *= other.volume;\r
+ return *this;\r
+}\r
+\r
+const audio_transform audio_transform::operator*(const audio_transform &other) const \r
+{\r
+ return audio_transform(*this) *= other;\r
+}\r
+\r
+struct audio_processor::implementation\r
+{\r
+ implementation()\r
+ {\r
+ transform_stack_.push(audio_transform());\r
+ }\r
+\r
+ void begin(const audio_transform& transform)\r
+ {\r
+ transform_stack_.push(transform_stack_.top()*transform);\r
+ }\r
+\r
+ void process(const std::vector<short>& audio_data)\r
+ { \r
+ if(audio_data_.empty())\r
+ audio_data_.resize(audio_data.size(), 0);\r
+\r
+ tbb::parallel_for\r
+ (\r
+ tbb::blocked_range<size_t>(0, audio_data.size()),\r
+ [&](const tbb::blocked_range<size_t>& r)\r
+ {\r
+ for(size_t n = r.begin(); n < r.end(); ++n)\r
+ {\r
+ int sample = static_cast<int>(audio_data[n]);\r
+ sample = (static_cast<int>(transform_stack_.top().volume*8192.0)*sample)/8192;\r
+ audio_data_[n] = static_cast<short>((static_cast<int>(audio_data_[n]) + sample) & 0xFFFF);\r
+ }\r
+ }\r
+ );\r
+ }\r
+\r
+ void end()\r
+ {\r
+ transform_stack_.pop();\r
+ }\r
+\r
+ std::vector<short> read()\r
+ {\r
+ return std::move(audio_data_);\r
+ }\r
+\r
+ std::vector<short> audio_data_;\r
+ std::stack<audio_transform> transform_stack_;\r
+};\r
+\r
+audio_processor::audio_processor() : impl_(new implementation()){}\r
+void audio_processor::begin(const audio_transform& transform){impl_->begin(transform);}\r
+void audio_processor::process(const std::vector<short>& audio_data){impl_->process(audio_data);}\r
+void audio_processor::end(){impl_->end();}\r
+std::vector<short> audio_processor::read(){return impl_->read();}\r
\r
}}
\ No newline at end of file
\r
namespace caspar { namespace core {\r
\r
+struct audio_transform\r
+{\r
+ audio_transform() : volume(1.0){}\r
+ double volume;\r
+\r
+ audio_transform& operator*=(const audio_transform &other);\r
+ const audio_transform operator*(const audio_transform &other) const;\r
+};\r
+\r
class audio_processor\r
{\r
public:\r
+ audio_processor();\r
+\r
+ void begin(const audio_transform& transform);\r
+ void process(const std::vector<short>& audio_data);\r
+ void end();\r
+\r
+ std::vector<short> read();\r
+\r
+ void begin_pass(){}\r
+ void end_pass(){}\r
\r
private:\r
+ struct implementation;\r
+ std::shared_ptr<implementation> impl_;\r
};\r
\r
}}
\ No newline at end of file
#include "draw_frame.h"\r
#include "composite_frame.h"\r
#include "transform_frame.h"\r
-#include "image_shader.h"\r
+#include "image_processor.h"\r
#include "../../common/utility/singleton_pool.h"\r
\r
#include <boost/range/algorithm.hpp>\r
\r
namespace caspar { namespace core {\r
\r
-struct composite_frame::implementation : public draw_frame_decorator\r
-{ \r
- implementation(std::vector<safe_ptr<draw_frame>>&& frames) : frames_(std::move(frames))\r
- { \r
- if(frames_.size() < 2)\r
- return;\r
-\r
- boost::range::for_each(frames_, [&](const safe_ptr<draw_frame>& frame)\r
- {\r
- if(audio_data_.empty())\r
- audio_data_ = frame->audio_data();\r
- else\r
- {\r
- tbb::parallel_for\r
- (\r
- tbb::blocked_range<size_t>(0, frame->audio_data().size()),\r
- [&](const tbb::blocked_range<size_t>& r)\r
- {\r
- for(size_t n = r.begin(); n < r.end(); ++n) \r
- audio_data_[n] = static_cast<short>((static_cast<int>(audio_data_[n]) + static_cast<int>(frame->audio_data()[n])) & 0xFFFF); \r
- }\r
- );\r
- }\r
- });\r
- }\r
- \r
- void unmap()\r
- {\r
- boost::range::for_each(frames_, std::bind(&draw_frame_decorator::unmap, std::placeholders::_1));\r
- }\r
- \r
- void draw(image_shader& shader)\r
+struct composite_frame::implementation\r
+{ \r
+ implementation(const std::vector<safe_ptr<draw_frame>>& frames) : frames_(frames){}\r
+ \r
+ void process_image(image_processor& processor)\r
{\r
- boost::range::for_each(frames_, std::bind(&draw_frame_decorator::draw, std::placeholders::_1, std::ref(shader)));\r
+ boost::range::for_each(frames_, std::bind(&draw_frame::process_image, std::placeholders::_1, std::ref(processor)));\r
}\r
\r
- const std::vector<short>& audio_data() const\r
+ void process_audio(audio_processor& processor)\r
{\r
- static std::vector<short> no_audio;\r
- return !audio_data_.empty() ? audio_data_ : (!frames_.empty() ? frames_.front()->audio_data() : no_audio);\r
+ boost::range::for_each(frames_, std::bind(&draw_frame::process_audio, std::placeholders::_1, std::ref(processor)));\r
}\r
- \r
- std::vector<short> audio_data_;\r
+ \r
std::vector<safe_ptr<draw_frame>> frames_;\r
};\r
\r
-composite_frame::composite_frame(std::vector<safe_ptr<draw_frame>>&& frames) : impl_(singleton_pool<implementation>::make_shared(std::move(frames))){}\r
+composite_frame::composite_frame(const std::vector<safe_ptr<draw_frame>>& frames) : impl_(singleton_pool<implementation>::make_shared(frames)){}\r
composite_frame::composite_frame(composite_frame&& other) : impl_(std::move(other.impl_)){}\r
composite_frame::composite_frame(const composite_frame& other) : impl_(singleton_pool<implementation>::make_shared(*other.impl_)){}\r
void composite_frame::swap(composite_frame& other){impl_.swap(other.impl_);}\r
impl_.reset(new implementation(std::move(frames)));\r
}\r
\r
-void composite_frame::unmap(){impl_->unmap();}\r
-void composite_frame::draw(image_shader& shader){impl_->draw(shader);}\r
-const std::vector<short>& composite_frame::audio_data() const {return impl_->audio_data();}\r
+void composite_frame::process_image(image_processor& processor){impl_->process_image(processor);}\r
+void composite_frame::process_audio(audio_processor& processor){impl_->process_audio(processor);}\r
\r
safe_ptr<composite_frame> composite_frame::interlace(safe_ptr<draw_frame>&& frame1, safe_ptr<draw_frame>&& frame2, video_mode::type mode)\r
{ \r
std::vector<safe_ptr<draw_frame>> frames;\r
frames.push_back(std::move(my_frame1));\r
frames.push_back(std::move(my_frame2));\r
- return make_safe<composite_frame>(std::move(frames));\r
+ return make_safe<composite_frame>(frames);\r
}\r
\r
}}
\ No newline at end of file
class composite_frame : public draw_frame\r
{\r
public:\r
- explicit composite_frame(std::vector<safe_ptr<draw_frame>>&& frames);\r
+ composite_frame(const std::vector<safe_ptr<draw_frame>>& frames);\r
composite_frame(safe_ptr<draw_frame>&& frame1, safe_ptr<draw_frame>&& frame2);\r
\r
void swap(composite_frame& other);\r
composite_frame& operator=(composite_frame&& other);\r
\r
static safe_ptr<composite_frame> interlace(safe_ptr<draw_frame>&& frame1, safe_ptr<draw_frame>&& frame2, video_mode::type mode);\r
- \r
- virtual const std::vector<short>& audio_data() const;\r
-\r
+ \r
private: \r
- virtual void unmap();\r
- virtual void draw(image_shader& shader);\r
+ virtual void process_image(image_processor& processor);\r
+ virtual void process_audio(audio_processor& processor);\r
\r
struct implementation;\r
std::shared_ptr<implementation> impl_;\r
#include "fwd.h"\r
\r
#include <boost/noncopyable.hpp>\r
-#include <boost/range/iterator_range.hpp>\r
-#include <boost/operators.hpp>\r
-\r
-#include <memory>\r
-#include <vector>\r
-#include <type_traits>\r
\r
#include "../../common/utility/safe_ptr.h"\r
\r
namespace caspar { namespace core {\r
\r
-struct draw_frame_decorator;\r
-\r
class draw_frame : boost::noncopyable\r
{ \r
- friend struct draw_frame_decorator;\r
- friend class image_processor;\r
public:\r
virtual ~draw_frame(){}\r
-\r
- virtual const std::vector<short>& audio_data() const = 0; \r
-\r
+ \r
static safe_ptr<draw_frame> eof()\r
{\r
struct eof_frame : public draw_frame\r
{\r
- virtual const std::vector<short>& audio_data() const\r
- {\r
- static std::vector<short> audio_data;\r
- return audio_data;\r
- }\r
- virtual void unmap(){}\r
- virtual void draw(image_shader&){}\r
+ virtual void process_image(image_processor&){}\r
+ virtual void process_audio(audio_processor&){}\r
};\r
static safe_ptr<draw_frame> frame = make_safe<eof_frame>();\r
return frame;\r
{\r
struct empty_frame : public draw_frame\r
{\r
- virtual const std::vector<short>& audio_data() const\r
- {\r
- static std::vector<short> audio_data;\r
- return audio_data;\r
- }\r
- virtual void unmap(){}\r
- virtual void draw(image_shader&){}\r
+ virtual void process_image(image_processor&){}\r
+ virtual void process_audio(audio_processor&){}\r
};\r
static safe_ptr<draw_frame> frame = make_safe<empty_frame>();\r
return frame;\r
}\r
-private:\r
- virtual void unmap() = 0;\r
- virtual void draw(image_shader& shader) = 0;\r
-};\r
\r
-struct draw_frame_decorator\r
-{\r
-protected:\r
- static void unmap(const safe_ptr<draw_frame>& frame) {frame->unmap();}\r
- static void draw(const safe_ptr<draw_frame>& frame, image_shader& shader) {frame->draw(shader);}\r
+ virtual void process_image(image_processor& processor) = 0;\r
+ virtual void process_audio(audio_processor& processor) = 0;\r
};\r
\r
\r
+\r
}}
\ No newline at end of file
\r
#include "frame_processor_device.h"\r
\r
+#include "audio_processor.h"\r
#include "image_processor.h"\r
+\r
#include "write_frame.h"\r
#include "read_frame.h"\r
\r
#include "../../common/concurrency/executor.h"\r
#include "../../common/gl/utility.h"\r
\r
+\r
#include <tbb/concurrent_queue.h>\r
#include <tbb/concurrent_unordered_map.h>\r
\r
{ \r
typedef boost::shared_future<safe_ptr<const read_frame>> output_type;\r
\r
- implementation(const video_format_desc& format_desc) : fmt_(format_desc)\r
+ implementation(const video_format_desc& format_desc) : fmt_(format_desc), writing_(draw_frame::empty()), image_processor_(format_desc)\r
{ \r
output_.set_capacity(3);\r
executor_.start();\r
- executor_.invoke([=]{image_processor_.reset(new image_processor(format_desc));});\r
}\r
\r
void send(safe_ptr<draw_frame>&& frame)\r
{ \r
- output_.push(executor_.begin_invoke([=]{return image_processor_->render(safe_ptr<draw_frame>(frame));})); // Blocks\r
+ output_.push(executor_.begin_invoke([=]() -> safe_ptr<const read_frame>\r
+ { \r
+ auto result = image_processor_.read();\r
+ result->audio_data(audio_processor_.read());\r
+\r
+ image_processor_.begin_pass();\r
+ writing_->process_image(image_processor_);\r
+ image_processor_.end_pass();\r
+\r
+ audio_processor_.begin_pass();\r
+ writing_->process_audio(audio_processor_);\r
+ audio_processor_.end_pass();\r
+\r
+ writing_ = frame;\r
+\r
+ return result;\r
+ }));\r
}\r
\r
safe_ptr<const read_frame> receive()\r
frame = executor_.invoke([&]{return std::make_shared<write_frame>(desc);}); \r
return safe_ptr<write_frame>(frame.get(), [=](write_frame*)\r
{\r
- executor_.begin_invoke([=]{frame->map();});\r
- frame->audio_data().clear();\r
- pool->push(frame);\r
+ executor_.begin_invoke([=]\r
+ {\r
+ frame->map();\r
+ pool->push(frame);\r
+ }, executor::high_priority);\r
});\r
}\r
+ \r
+ const video_format_desc format_desc_;\r
+ \r
+ safe_ptr<draw_frame> writing_;\r
+\r
+ audio_processor audio_processor_;\r
+ image_processor image_processor_;\r
\r
executor executor_; \r
-\r
- std::unique_ptr<image_processor> image_processor_; \r
\r
tbb::concurrent_bounded_queue<output_type> output_; \r
tbb::concurrent_unordered_map<pixel_format_desc, tbb::concurrent_bounded_queue<std::shared_ptr<write_frame>>, std::hash<pixel_format_desc>> pools_;\r
class transform_frame;\r
class composite_frame;\r
\r
-class image_shader;\r
class image_processor;\r
+class frame_processor;\r
+class audio_processor;\r
class frame_processor_device;\r
}}
\ No newline at end of file
\r
#include "image_processor.h"\r
\r
-#include "image_shader.h"\r
-#include "write_frame.h"\r
-#include "read_frame.h"\r
-\r
-#include "../format/video_format.h"\r
-\r
#include "../../common/exception/exceptions.h"\r
#include "../../common/gl/utility.h"\r
+#include "../../common/gl/shader_program.h"\r
#include "../../common/gl/frame_buffer_object.h"\r
\r
#include <Glee.h>\r
#include <SFML/Window.hpp>\r
\r
-#include <tbb/concurrent_queue.h>\r
+#include <boost/noncopyable.hpp>\r
\r
-#include <boost/range/algorithm_ext/erase.hpp>\r
-#include <boost/range/algorithm.hpp>\r
-\r
-#include <functional>\r
-\r
-namespace caspar { namespace core {\r
+#include <unordered_map>\r
\r
struct ogl_context\r
{\r
sf::Context context_;\r
};\r
\r
+namespace caspar { namespace core {\r
+\r
+GLubyte progressive_pattern[] = {\r
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,\r
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,\r
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,\r
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xFF, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff};\r
+ \r
+GLubyte upper_pattern[] = {\r
+ 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00,\r
+ 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00,\r
+ 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00,\r
+ 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00};\r
+ \r
+GLubyte lower_pattern[] = {\r
+ 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, \r
+ 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff,\r
+ 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff,\r
+ 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff};\r
+ \r
struct image_processor::implementation : boost::noncopyable\r
-{ \r
- implementation(const video_format_desc& format_desc) : shader_(format_desc), format_desc_(format_desc),\r
- fbo_(format_desc.width, format_desc.height), writing_(draw_frame::empty()), reading_(create_reading())\r
- { \r
+{\r
+ implementation(const video_format_desc& format_desc) : fbo_(format_desc.width, format_desc.height), current_(pixel_format::invalid), reading_(create_reading())\r
+ {\r
+ alpha_stack_.push(1.0);\r
+ GL(glEnable(GL_POLYGON_STIPPLE));\r
+ GL(glEnable(GL_TEXTURE_2D));\r
+ GL(glEnable(GL_BLEND));\r
+ GL(glDisable(GL_DEPTH_TEST));\r
+ GL(glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)); \r
+ GL(glViewport(0, 0, format_desc.width, format_desc.height));\r
reading_->unmap();\r
+\r
+ std::string common_vertex = \r
+ "void main() "\r
+ "{ "\r
+ " gl_TexCoord[0] = gl_MultiTexCoord0; "\r
+ " gl_FrontColor = gl_Color; "\r
+ " gl_Position = ftransform(); "\r
+ "} ";\r
+\r
+ std::string common_fragment = \r
+ "uniform sampler2D plane[4]; "\r
+ "uniform vec4 plane_size[2]; "\r
+ \r
+ // NOTE: YCbCr, ITU-R, http://www.intersil.com/data/an/an9717.pdf \r
+ // TODO: Support for more yuv formats might be needed. \r
+ "vec4 ycbcra_to_bgra(float y, float cb, float cr, float a) "\r
+ "{ "\r
+ " cb -= 0.5; "\r
+ " cr -= 0.5; "\r
+ " y = 1.164*(y-0.0625); "\r
+ " "\r
+ " vec4 color; "\r
+ " color.r = y + 1.596 * cr; "\r
+ " color.g = y - 0.813 * cr - 0.337633 * cb; "\r
+ " color.b = y + 2.017 * cb; "\r
+ " color.a = a; "\r
+ " "\r
+ " return color; "\r
+ "} " \r
+ " ";\r
+ \r
+ shaders_[pixel_format::abgr] = gl::shader_program(common_vertex, common_fragment +\r
+\r
+ "void main() "\r
+ "{ "\r
+ " vec4 abgr = texture2D(plane[0], gl_TexCoord[0].st);"\r
+ " gl_FragColor = abgr.argb * gl_Color; "\r
+ "} ");\r
+ \r
+ shaders_[pixel_format::argb]= gl::shader_program(common_vertex, common_fragment +\r
+\r
+ "void main() " \r
+ "{ "\r
+ " vec4 argb = texture2D(plane[0], gl_TexCoord[0].st);"\r
+ " gl_FragColor = argb.grab * gl_Color; " \r
+ "} ");\r
+ \r
+ shaders_[pixel_format::bgra]= gl::shader_program(common_vertex, common_fragment +\r
+\r
+ "void main() "\r
+ "{ "\r
+ " vec4 bgra = texture2D(plane[0], gl_TexCoord[0].st);"\r
+ " gl_FragColor = bgra.rgba * gl_Color; "\r
+ "} ");\r
+ \r
+ shaders_[pixel_format::rgba] = gl::shader_program(common_vertex, common_fragment +\r
+\r
+ "void main() "\r
+ "{ "\r
+ " vec4 rgba = texture2D(plane[0], gl_TexCoord[0].st); "\r
+ " gl_FragColor = rgba.bgra * gl_Color; "\r
+ "} ");\r
+ \r
+ shaders_[pixel_format::ycbcr] = gl::shader_program(common_vertex, common_fragment +\r
+\r
+ "void main() "\r
+ "{ "\r
+ " float y = texture2D(plane[0], gl_TexCoord[0].st).r;"\r
+ " float cb = texture2D(plane[1], gl_TexCoord[0].st).r;"\r
+ " float cr = texture2D(plane[2], gl_TexCoord[0].st).r;"\r
+ " float a = 1.0; " \r
+ " gl_FragColor = ycbcra_to_bgra(y, cb, cr, a) * gl_Color; "\r
+ "} ");\r
+ \r
+ shaders_[pixel_format::ycbcra] = gl::shader_program(common_vertex, common_fragment +\r
+\r
+ "void main() "\r
+ "{ "\r
+ " float y = texture2D(plane[0], gl_TexCoord[0].st).r;"\r
+ " float cb = texture2D(plane[1], gl_TexCoord[0].st).r;"\r
+ " float cr = texture2D(plane[2], gl_TexCoord[0].st).r;"\r
+ " float a = texture2D(plane[3], gl_TexCoord[0].st).r;"\r
+ " gl_FragColor = ycbcra_to_bgra(y, cb, cr, a) * gl_Color; "\r
+ "} ");\r
}\r
- \r
- safe_ptr<const read_frame> render(safe_ptr<draw_frame>&& frame)\r
- { \r
- frame->unmap(); // Start transfer from system memory to texture. End with draw in next tick.\r
\r
- reading_->map(); // Map texture to system memory.\r
- auto result = reading_;\r
+ void begin(const image_transform& transform)\r
+ {\r
+ alpha_stack_.push(alpha_stack_.top()*transform.alpha);\r
\r
- GL(glClear(GL_COLOR_BUFFER_BIT));\r
+ glPushMatrix();\r
+ glColor4d(1.0, 1.0, 1.0, alpha_stack_.top());\r
+ glTranslated(transform.pos.get<0>()*2.0, transform.pos.get<1>()*2.0, 0.0);\r
+\r
+ if(transform.mode == video_mode::upper)\r
+ glPolygonStipple(upper_pattern);\r
+ else if(transform.mode == video_mode::lower)\r
+ glPolygonStipple(lower_pattern);\r
+ else\r
+ glPolygonStipple(progressive_pattern);\r
+ }\r
+ \r
+ void render(const pixel_format_desc& desc, std::vector<gl::pbo>& pbos)\r
+ {\r
+ set_pixel_format(desc.pix_fmt);\r
+\r
+ for(size_t n = 0; n < pbos.size(); ++n)\r
+ {\r
+ glActiveTexture(GL_TEXTURE0+n);\r
+ pbos[n].unmap_write();\r
+ }\r
\r
- writing_->draw(shader_); // Draw to frame buffer.\r
- writing_ = frame;\r
+ glBegin(GL_QUADS);\r
+ glTexCoord2d(0.0, 0.0); glVertex2d(-1.0, -1.0);\r
+ glTexCoord2d(1.0, 0.0); glVertex2d( 1.0, -1.0);\r
+ glTexCoord2d(1.0, 1.0); glVertex2d( 1.0, 1.0);\r
+ glTexCoord2d(0.0, 1.0); glVertex2d(-1.0, 1.0);\r
+ glEnd();\r
+ }\r
\r
+ void end()\r
+ {\r
+ alpha_stack_.pop();\r
+ glPopMatrix();\r
+ }\r
+\r
+ void begin_pass()\r
+ {\r
+ GL(glClear(GL_COLOR_BUFFER_BIT));\r
+ }\r
+\r
+ void end_pass()\r
+ {\r
reading_ = create_reading();\r
reading_->unmap(); // Start transfer from frame buffer to texture. End with map in next tick.\r
- reading_->audio_data(writing_->audio_data());\r
+ }\r
+\r
+ safe_ptr<read_frame> read()\r
+ {\r
+ reading_->map();\r
+ return reading_;\r
+ }\r
\r
- return result;\r
+ void set_pixel_format(pixel_format::type format)\r
+ {\r
+ if(current_ == format)\r
+ return;\r
+ current_ = format;\r
+ shaders_[format].use();\r
}\r
\r
safe_ptr<read_frame> create_reading()\r
{\r
std::shared_ptr<read_frame> frame;\r
if(!pool_.try_pop(frame)) \r
- frame = std::make_shared<read_frame>(format_desc_.width, format_desc_.height);\r
+ frame = std::make_shared<read_frame>(fbo_.width(), fbo_.height());\r
return safe_ptr<read_frame>(frame.get(), [=](read_frame*){pool_.push(frame);});\r
}\r
\r
const ogl_context context_;\r
- const gl::fbo fbo_;\r
- const video_format_desc format_desc_;\r
-\r
+ \r
tbb::concurrent_bounded_queue<std::shared_ptr<read_frame>> pool_;\r
- \r
- safe_ptr<read_frame> reading_;\r
- safe_ptr<draw_frame> writing_;\r
\r
- image_shader shader_;\r
+ std::stack<double> alpha_stack_;\r
+\r
+ pixel_format::type current_;\r
+ std::unordered_map<pixel_format::type, gl::shader_program> shaders_;\r
+ gl::fbo fbo_;\r
+\r
+ safe_ptr<read_frame> reading_;\r
};\r
- \r
-image_processor::image_processor(const video_format_desc& format_desc) : impl_(new implementation(format_desc)){}\r
-safe_ptr<const read_frame> image_processor::render(safe_ptr<draw_frame>&& frame){return impl_->render(std::move(frame));}\r
+\r
+image_processor::image_processor(const video_format_desc& format_desc) : format_desc_(format_desc){}\r
+void image_processor::begin(const image_transform& transform) \r
+{\r
+ if(!impl_)\r
+ impl_.reset(new implementation(format_desc_));\r
+ impl_->begin(transform);\r
+}\r
+void image_processor::process(const pixel_format_desc& desc, std::vector<gl::pbo>& pbos)\r
+{\r
+ if(!impl_)\r
+ impl_.reset(new implementation(format_desc_));\r
+ impl_->render(desc, pbos);\r
+}\r
+void image_processor::end()\r
+{\r
+ if(!impl_)\r
+ impl_.reset(new implementation(format_desc_));\r
+ impl_->end();\r
+}\r
+void image_processor::begin_pass()\r
+{\r
+ if(!impl_)\r
+ impl_.reset(new implementation(format_desc_));\r
+ impl_->begin_pass();\r
+}\r
+void image_processor::end_pass()\r
+{\r
+ if(!impl_)\r
+ impl_.reset(new implementation(format_desc_));\r
+ impl_->end_pass();\r
+}\r
+safe_ptr<read_frame> image_processor::read()\r
+{\r
+ if(!impl_)\r
+ impl_.reset(new implementation(format_desc_));\r
+ return impl_->read();\r
+}\r
}}
\ No newline at end of file
-/*\r
-* copyright (c) 2010 Sveriges Television AB <info@casparcg.com>\r
-*\r
-* This file is part of CasparCG.\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
-*\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
-*/\r
#pragma once\r
\r
-#include "fwd.h"\r
-\r
-#include "../../common/utility/safe_ptr.h"\r
+#include "read_frame.h"\r
\r
#include "../format/video_format.h"\r
+#include "../format/pixel_format.h"\r
\r
-#include <boost/noncopyable.hpp>\r
+#include <boost/tuple/tuple.hpp>\r
\r
#include <memory>\r
+#include <array>\r
\r
namespace caspar { namespace core {\r
+ \r
+struct image_transform\r
+{\r
+ image_transform() : alpha(1.0), pos(boost::make_tuple(0.0, 0.0)), uv(boost::make_tuple(0.0, 1.0, 1.0, 0.0)), mode(video_mode::invalid){}\r
+ double alpha;\r
+ boost::tuple<double, double> pos;\r
+ boost::tuple<double, double, double, double> uv;\r
+ video_mode::type mode; \r
+};\r
\r
-class image_processor : boost::noncopyable\r
+class image_processor\r
{\r
public:\r
- image_processor(const video_format_desc& format_desc_);\r
- \r
- safe_ptr<const read_frame> render(safe_ptr<draw_frame>&& frame);\r
+ image_processor(const video_format_desc& format_desc);\r
+\r
+ void begin(const image_transform& transform);\r
+ void process(const pixel_format_desc& desc, std::vector<gl::pbo>& pbos);\r
+ void end();\r
+\r
+ void begin_pass();\r
+ void end_pass();\r
+ safe_ptr<read_frame> read();\r
private:\r
struct implementation;\r
std::shared_ptr<implementation> impl_;\r
+ video_format_desc format_desc_;\r
};\r
-typedef std::shared_ptr<image_processor> image_processor_ptr;\r
+typedef std::shared_ptr<image_processor> image_shader_ptr;\r
\r
}}
\ No newline at end of file
+++ /dev/null
-#include "../StdAfx.h"\r
-\r
-#include "image_shader.h"\r
-\r
-#include "../../common/exception/exceptions.h"\r
-#include "../../common/gl/utility.h"\r
-#include "../../common/gl/shader_program.h"\r
-\r
-#include <Glee.h>\r
-\r
-#include <boost/noncopyable.hpp>\r
-\r
-#include <unordered_map>\r
-\r
-namespace caspar { namespace core {\r
-\r
-GLubyte progressive_pattern[] = {\r
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,\r
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,\r
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,\r
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xFF, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff};\r
- \r
-GLubyte upper_pattern[] = {\r
- 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00,\r
- 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00,\r
- 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00,\r
- 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00};\r
- \r
-GLubyte lower_pattern[] = {\r
- 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, \r
- 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff,\r
- 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff,\r
- 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff};\r
- \r
-struct image_shader::implementation : boost::noncopyable\r
-{\r
- implementation(const video_format_desc& format_desc) : current_(pixel_format::invalid), alpha_(1.0)\r
- {\r
- GL(glEnable(GL_POLYGON_STIPPLE));\r
- GL(glEnable(GL_TEXTURE_2D));\r
- GL(glEnable(GL_BLEND));\r
- GL(glDisable(GL_DEPTH_TEST));\r
- GL(glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)); \r
- GL(glViewport(0, 0, format_desc.width, format_desc.height));\r
-\r
- transform_stack_.push(shader_transform());\r
- std::string common_vertex = \r
- "void main() "\r
- "{ "\r
- " gl_TexCoord[0] = gl_MultiTexCoord0; "\r
- " gl_FrontColor = gl_Color; "\r
- " gl_Position = ftransform(); "\r
- "} ";\r
-\r
- std::string common_fragment = \r
- "uniform sampler2D plane[4]; "\r
- "uniform vec4 plane_size[2]; "\r
- \r
- // NOTE: YCbCr, ITU-R, http://www.intersil.com/data/an/an9717.pdf \r
- // TODO: Support for more yuv formats might be needed. \r
- "vec4 ycbcra_to_bgra(float y, float cb, float cr, float a) "\r
- "{ "\r
- " cb -= 0.5; "\r
- " cr -= 0.5; "\r
- " y = 1.164*(y-0.0625); "\r
- " "\r
- " vec4 color; "\r
- " color.r = y + 1.596 * cr; "\r
- " color.g = y - 0.813 * cr - 0.337633 * cb; "\r
- " color.b = y + 2.017 * cb; "\r
- " color.a = a; "\r
- " "\r
- " return color; "\r
- "} " \r
- " ";\r
- \r
- shaders_[pixel_format::abgr] = gl::shader_program(common_vertex, common_fragment +\r
-\r
- "void main() "\r
- "{ "\r
- " vec4 abgr = texture2D(plane[0], gl_TexCoord[0].st);"\r
- " gl_FragColor = abgr.argb * gl_Color; "\r
- "} ");\r
- \r
- shaders_[pixel_format::argb]= gl::shader_program(common_vertex, common_fragment +\r
-\r
- "void main() " \r
- "{ "\r
- " vec4 argb = texture2D(plane[0], gl_TexCoord[0].st);"\r
- " gl_FragColor = argb.grab * gl_Color; " \r
- "} ");\r
- \r
- shaders_[pixel_format::bgra]= gl::shader_program(common_vertex, common_fragment +\r
-\r
- "void main() "\r
- "{ "\r
- " vec4 bgra = texture2D(plane[0], gl_TexCoord[0].st);"\r
- " gl_FragColor = bgra.rgba * gl_Color; "\r
- "} ");\r
- \r
- shaders_[pixel_format::rgba] = gl::shader_program(common_vertex, common_fragment +\r
-\r
- "void main() "\r
- "{ "\r
- " vec4 rgba = texture2D(plane[0], gl_TexCoord[0].st); "\r
- " gl_FragColor = rgba.bgra * gl_Color; "\r
- "} ");\r
- \r
- shaders_[pixel_format::ycbcr] = gl::shader_program(common_vertex, common_fragment +\r
-\r
- "void main() "\r
- "{ "\r
- " float y = texture2D(plane[0], gl_TexCoord[0].st).r;"\r
- " float cb = texture2D(plane[1], gl_TexCoord[0].st).r;"\r
- " float cr = texture2D(plane[2], gl_TexCoord[0].st).r;"\r
- " float a = 1.0; " \r
- " gl_FragColor = ycbcra_to_bgra(y, cb, cr, a) * gl_Color; "\r
- "} ");\r
- \r
- shaders_[pixel_format::ycbcra] = gl::shader_program(common_vertex, common_fragment +\r
-\r
- "void main() "\r
- "{ "\r
- " float y = texture2D(plane[0], gl_TexCoord[0].st).r;"\r
- " float cb = texture2D(plane[1], gl_TexCoord[0].st).r;"\r
- " float cr = texture2D(plane[2], gl_TexCoord[0].st).r;"\r
- " float a = texture2D(plane[3], gl_TexCoord[0].st).r;"\r
- " gl_FragColor = ycbcra_to_bgra(y, cb, cr, a) * gl_Color; "\r
- "} ");\r
- }\r
-\r
- void begin(const shader_transform& transform)\r
- {\r
- transform_stack_.push(transform);\r
-\r
- glPushMatrix();\r
- alpha_ *= transform_stack_.top().alpha;\r
- glColor4d(1.0, 1.0, 1.0, alpha_);\r
- glTranslated(transform_stack_.top().pos.get<0>()*2.0, transform_stack_.top().pos.get<1>()*2.0, 0.0);\r
-\r
- if(transform.mode == video_mode::upper)\r
- glPolygonStipple(upper_pattern);\r
- else if(transform.mode == video_mode::lower)\r
- glPolygonStipple(lower_pattern);\r
- else\r
- glPolygonStipple(progressive_pattern);\r
- }\r
- \r
- void render(const pixel_format_desc& desc)\r
- {\r
- set_pixel_format(desc.pix_fmt);\r
-\r
- auto t = transform_stack_.top();\r
- glBegin(GL_QUADS);\r
- glTexCoord2d(0.0, 0.0); glVertex2d(-1.0, -1.0);\r
- glTexCoord2d(1.0, 0.0); glVertex2d( 1.0, -1.0);\r
- glTexCoord2d(1.0, 1.0); glVertex2d( 1.0, 1.0);\r
- glTexCoord2d(0.0, 1.0); glVertex2d(-1.0, 1.0);\r
- glEnd();\r
- }\r
-\r
- void end()\r
- {\r
- alpha_ /= transform_stack_.top().alpha;\r
- glPopMatrix();\r
- transform_stack_.pop();\r
- }\r
-\r
- void set_pixel_format(pixel_format::type format)\r
- {\r
- if(current_ == format)\r
- return;\r
- current_ = format;\r
- shaders_[format].use();\r
- }\r
- \r
- double alpha_;\r
- std::stack<shader_transform> transform_stack_;\r
-\r
- pixel_format::type current_;\r
- std::unordered_map<pixel_format::type, gl::shader_program> shaders_;\r
-};\r
-\r
-image_shader::image_shader(const video_format_desc& format_desc) : impl_(new implementation(format_desc)){}\r
-void image_shader::begin(const shader_transform& transform) {impl_->begin(transform);}\r
-void image_shader::render(const pixel_format_desc& desc){impl_->render(desc);}\r
-void image_shader::end(){impl_->end();}\r
-}}
\ No newline at end of file
+++ /dev/null
-#pragma once\r
-\r
-#include "../format/video_format.h"\r
-#include "../format/pixel_format.h"\r
-\r
-#include <boost/tuple/tuple.hpp>\r
-\r
-#include <memory>\r
-#include <array>\r
-\r
-namespace caspar { namespace core {\r
- \r
-struct shader_transform\r
-{\r
- shader_transform() : alpha(1.0), pos(boost::make_tuple(0.0, 0.0)), uv(boost::make_tuple(0.0, 1.0, 1.0, 0.0)), mode(video_mode::invalid){}\r
- double alpha;\r
- boost::tuple<double, double> pos;\r
- boost::tuple<double, double, double, double> uv;\r
- video_mode::type mode; \r
-};\r
-\r
-class image_shader\r
-{\r
-public:\r
- image_shader(const video_format_desc& format_desc);\r
-\r
- void begin(const shader_transform& transform);\r
- void render(const pixel_format_desc& desc);\r
- void end();\r
-private:\r
- struct implementation;\r
- std::shared_ptr<implementation> impl_;\r
-};\r
-typedef std::shared_ptr<image_shader> image_shader_ptr;\r
-\r
-}}
\ No newline at end of file
\r
class read_frame\r
{\r
+ friend class image_processor;\r
public:\r
explicit read_frame(size_t width, size_t height);\r
\r
const std::vector<short>& audio_data() const;\r
void audio_data(const std::vector<short>& audio_data);\r
\r
+private:\r
void unmap();\r
void map();\r
\r
-private:\r
struct implementation;\r
std::shared_ptr<implementation> impl_;\r
};\r
#include "transform_frame.h"\r
\r
#include "draw_frame.h"\r
-#include "image_shader.h"\r
+#include "image_processor.h"\r
+#include "audio_processor.h"\r
\r
#include "../format/pixel_format.h"\r
#include "../../common/gl/utility.h"\r
\r
namespace caspar { namespace core {\r
\r
-struct transform_frame::implementation : public draw_frame_decorator\r
+struct transform_frame::implementation\r
{\r
- implementation(const safe_ptr<draw_frame>& frame) : frame_(frame), audio_volume_(255){}\r
- implementation(const safe_ptr<draw_frame>& frame, std::vector<short>&& audio_data) : frame_(frame), audio_volume_(255), override_audio_data_(std::move(audio_data)){}\r
- implementation(safe_ptr<draw_frame>&& frame) : frame_(std::move(frame)), audio_volume_(255){}\r
+ implementation(const safe_ptr<draw_frame>& frame) : frame_(frame){}\r
+ implementation(safe_ptr<draw_frame>&& frame) : frame_(std::move(frame)){}\r
\r
- void unmap(){draw_frame_decorator::unmap(frame_);}\r
-\r
- void draw(image_shader& shader)\r
+ void process_image(image_processor& processor)\r
{\r
- shader.begin(transform_);\r
- draw_frame_decorator::draw(frame_, shader);\r
- shader.end();\r
+ processor.begin(image_transform_);\r
+ frame_->process_image(processor);\r
+ processor.end();\r
}\r
\r
- void audio_volume(const unsigned char volume)\r
- {\r
- if(volume == audio_volume_)\r
- return;\r
- \r
- audio_volume_ = volume;\r
-\r
- auto& source = !override_audio_data_.empty() ? override_audio_data_ : frame_->audio_data();\r
- audio_data_.resize(source.size());\r
- tbb::parallel_for(tbb::blocked_range<size_t>(0, audio_data_.size()), [&](const tbb::blocked_range<size_t>& r)\r
- {\r
- for(size_t n = r.begin(); n < r.end(); ++n) \r
- audio_data_[n] = static_cast<short>((static_cast<int>(source[n])*volume)>>8); \r
- });\r
- }\r
- \r
- const std::vector<short>& audio_data() const \r
+ void process_audio(audio_processor& processor)\r
{\r
- return !audio_data_.empty() ? audio_data_ : (!override_audio_data_.empty() ? override_audio_data_ : frame_->audio_data());\r
+ processor.begin(audio_transform_);\r
+ frame_->process_audio(processor);\r
+ processor.end();\r
}\r
\r
- unsigned char audio_volume_;\r
safe_ptr<draw_frame> frame_;\r
std::vector<short> audio_data_;\r
std::vector<short> override_audio_data_;\r
- shader_transform transform_; \r
+ image_transform image_transform_; \r
+ audio_transform audio_transform_; \r
};\r
\r
transform_frame::transform_frame() : impl_(singleton_pool<implementation>::make_shared(draw_frame::empty())){}\r
transform_frame::transform_frame(const safe_ptr<draw_frame>& frame) : impl_(singleton_pool<implementation>::make_shared(frame)){}\r
-transform_frame::transform_frame(const safe_ptr<draw_frame>& frame, std::vector<short>&& audio_data) : impl_(singleton_pool<implementation>::make_shared(frame, std::move(audio_data))){}\r
transform_frame::transform_frame(safe_ptr<draw_frame>&& frame) : impl_(singleton_pool<implementation>::make_shared(std::move(frame))){}\r
transform_frame::transform_frame(const transform_frame& other) : impl_(singleton_pool<implementation>::make_shared(*other.impl_)){}\r
void transform_frame::swap(transform_frame& other){impl_.swap(other.impl_);}\r
temp.swap(*this);\r
return *this;\r
}\r
-void transform_frame::unmap(){impl_->unmap();} \r
-void transform_frame::draw(image_shader& shader){impl_->draw(shader);}\r
-void transform_frame::audio_volume(unsigned char volume){impl_->audio_volume(volume);}\r
-void transform_frame::translate(double x, double y){impl_->transform_.pos = boost::make_tuple(x, y);}\r
-void transform_frame::texcoord(double left, double top, double right, double bottom){impl_->transform_.uv = boost::make_tuple(left, top, right, bottom);}\r
-void transform_frame::video_mode(video_mode::type mode){impl_->transform_.mode = mode;}\r
-void transform_frame::alpha(double value){impl_->transform_.alpha = value;}\r
-const std::vector<short>& transform_frame::audio_data() const { return impl_->audio_data(); }\r
+void transform_frame::process_image(image_processor& processor){impl_->process_image(processor);}\r
+void transform_frame::process_audio(audio_processor& processor){impl_->process_audio(processor);}\r
+void transform_frame::audio_volume(double volume){impl_->audio_transform_.volume = volume;}\r
+void transform_frame::translate(double x, double y){impl_->image_transform_.pos = boost::make_tuple(x, y);}\r
+void transform_frame::texcoord(double left, double top, double right, double bottom){impl_->image_transform_.uv = boost::make_tuple(left, top, right, bottom);}\r
+void transform_frame::video_mode(video_mode::type mode){impl_->image_transform_.mode = mode;}\r
+void transform_frame::alpha(double value){impl_->image_transform_.alpha = value;}\r
}}
\ No newline at end of file
public:\r
transform_frame();\r
transform_frame(const safe_ptr<draw_frame>& frame);\r
- transform_frame(const safe_ptr<draw_frame>& frame, std::vector<short>&& audio_data);\r
transform_frame(safe_ptr<draw_frame>&& frame);\r
\r
void swap(transform_frame& other);\r
transform_frame& operator=(const transform_frame& other);\r
transform_frame& operator=(transform_frame&& other);\r
\r
- virtual const std::vector<short>& audio_data() const;\r
-\r
- void audio_volume(unsigned char volume);\r
+ void audio_volume(double volume);\r
void translate(double x, double y);\r
void texcoord(double left, double top, double right, double bottom);\r
void video_mode(video_mode::type mode);\r
void alpha(double value);\r
-\r
+ \r
private:\r
\r
- virtual void unmap();\r
- virtual void draw(image_shader& shader);\r
+ virtual void process_image(image_processor& processor);\r
+ virtual void process_audio(audio_processor& processor);\r
\r
struct implementation;\r
std::shared_ptr<implementation> impl_;\r
#include "write_frame.h"\r
\r
#include "draw_frame.h"\r
-#include "image_shader.h"\r
+#include "image_processor.h"\r
+#include "audio_processor.h"\r
\r
#include "../format/pixel_format.h"\r
#include "../../common/gl/utility.h"\r
boost::range::for_each(pbos_, std::mem_fn(&gl::pbo::map_write));\r
}\r
\r
- void draw(image_shader& shader)\r
+ void process_image(image_processor& processor)\r
{\r
- for(size_t n = 0; n < pbos_.size(); ++n)\r
- {\r
- glActiveTexture(GL_TEXTURE0+n);\r
- pbos_[n].bind_texture();\r
- }\r
- shader.render(desc_);\r
+ processor.process(desc_, pbos_);\r
+ }\r
+\r
+ void process_audio(audio_processor& processor)\r
+ {\r
+ processor.process(audio_data_);\r
+ audio_data_.clear();\r
}\r
\r
boost::iterator_range<unsigned char*> pixel_data(size_t index)\r
}\r
void write_frame::map(){impl_->map();} \r
void write_frame::unmap(){impl_->unmap();} \r
-void write_frame::draw(image_shader& shader){impl_->draw(shader);}\r
+void write_frame::process_image(image_processor& processor){impl_->process_image(processor);}\r
+void write_frame::process_audio(audio_processor& processor){impl_->process_audio(processor);}\r
boost::iterator_range<unsigned char*> write_frame::pixel_data(size_t index){return impl_->pixel_data(index);}\r
const boost::iterator_range<const unsigned char*> write_frame::pixel_data(size_t index) const {return impl_->pixel_data(index);}\r
std::vector<short>& write_frame::audio_data() { return impl_->audio_data_; }\r
-const std::vector<short>& write_frame::audio_data() const { return impl_->audio_data_; }\r
}}
\ No newline at end of file
\r
#include "draw_frame.h"\r
\r
-#include "../../common/exception/exceptions.h"\r
-#include "../../common/gl/pixel_buffer_object.h"\r
-\r
#include "../format/video_format.h"\r
#include "../format/pixel_format.h"\r
\r
#include <boost/noncopyable.hpp>\r
-#include <boost/tuple/tuple.hpp>\r
#include <boost/range/iterator_range.hpp>\r
\r
#include <memory>\r
-#include <array>\r
#include <vector>\r
\r
namespace caspar { namespace core {\r
const boost::iterator_range<const unsigned char*> pixel_data(size_t index = 0) const;\r
\r
std::vector<short>& audio_data();\r
- virtual const std::vector<short>& audio_data() const;\r
\r
private:\r
\r
void map();\r
void unmap();\r
- void draw(image_shader& shader);\r
+ void process_image(image_processor& processor);\r
+ void process_audio(audio_processor& processor);\r
\r
struct implementation;\r
std::shared_ptr<implementation> impl_;\r
struct ffmpeg_producer_impl\r
{\r
public:\r
- ffmpeg_producer_impl(const std::wstring& filename, const std::vector<std::wstring>& params) : filename_(filename), underrun_count_(0), last_frame_(draw_frame::empty())\r
+ ffmpeg_producer_impl(const std::wstring& filename, const std::vector<std::wstring>& params) : filename_(filename), underrun_count_(0), last_frame_(transform_frame(draw_frame::empty()))\r
{\r
if(!boost::filesystem::exists(filename))\r
BOOST_THROW_EXCEPTION(file_not_found() << boost::errinfo_file_name(narrow(filename)));\r
if(!video_packet.empty())\r
{\r
auto decoded_frame = video_decoder_->execute(video_packet);\r
- auto frame = make_safe<transform_frame>(video_transformer_->execute(decoded_frame));\r
+ auto frame = video_transformer_->execute(decoded_frame);\r
video_frame_channel_.push_back(std::move(frame)); \r
}\r
}, \r
audio_chunk_channel_.pop_front();\r
}\r
\r
- auto transform = transform_frame(std::move(video_frame_channel_.front()), std::move(audio_data));\r
+ auto write = std::move(video_frame_channel_.front());\r
+ write->audio_data() = std::move(audio_data);\r
+ auto transform = transform_frame(write);\r
video_frame_channel_.pop_front();\r
\r
// TODO: Make generic for all formats and modes.\r
if(!ouput_channel_.empty())\r
{\r
result = std::move(ouput_channel_.front());\r
- last_frame_ = make_safe<transform_frame>(result, std::vector<short>()); // last_frame should not have audio, override it!\r
+ last_frame_ = transform_frame(result);\r
+ last_frame_->audio_volume(0.0); // last_frame should not have audio\r
ouput_channel_.pop();\r
}\r
else if(input_->is_eof())\r
- last_frame_ = draw_frame::eof();\r
+ return draw_frame::eof();\r
\r
return result;\r
}\r
\r
video_decoder_uptr video_decoder_;\r
video_transformer_uptr video_transformer_;\r
- std::deque<safe_ptr<transform_frame>> video_frame_channel_;\r
+ std::deque<safe_ptr<write_frame>> video_frame_channel_;\r
\r
audio_decoder_ptr audio_decoder_;\r
std::deque<std::vector<short>> audio_chunk_channel_;\r
\r
long underrun_count_;\r
\r
- safe_ptr<draw_frame> last_frame_;\r
+ safe_ptr<transform_frame> last_frame_;\r
\r
video_format_desc format_desc_;\r
};\r
win32_exception::install_handler();\r
\r
is_running_ = true;\r
- AVPacket tmp_packet;\r
- while(is_running_)\r
- {\r
- std::shared_ptr<AVPacket> packet(&tmp_packet, av_free_packet); \r
- tbb::queuing_mutex::scoped_lock lock(seek_mutex_); \r
\r
- if (av_read_frame(format_context_.get(), packet.get()) >= 0) // NOTE: Packet is only valid until next call of av_safe_ptr<read_frame> or av_close_input_file\r
+ try\r
+ {\r
+ AVPacket tmp_packet;\r
+ while(is_running_)\r
{\r
- auto buffer = std::make_shared<aligned_buffer>(packet->data, packet->data + packet->size);\r
- if(packet->stream_index == video_s_index_) \r
- video_packet_buffer_.push(buffer); \r
- else if(packet->stream_index == audio_s_index_) \r
- audio_packet_buffer_.push(buffer); \r
+ std::shared_ptr<AVPacket> packet(&tmp_packet, av_free_packet); \r
+ tbb::queuing_mutex::scoped_lock lock(seek_mutex_); \r
+\r
+ if (av_read_frame(format_context_.get(), packet.get()) >= 0) // NOTE: Packet is only valid until next call of av_safe_ptr<read_frame> or av_close_input_file\r
+ {\r
+ auto buffer = std::make_shared<aligned_buffer>(packet->data, packet->data + packet->size);\r
+ if(packet->stream_index == video_s_index_) \r
+ video_packet_buffer_.push(buffer); \r
+ else if(packet->stream_index == audio_s_index_) \r
+ audio_packet_buffer_.push(buffer); \r
+ }\r
+ else if(!loop_ || av_seek_frame(format_context_.get(), -1, 0, AVSEEK_FLAG_BACKWARD) < 0) // TODO: av_seek_frame does not work for all formats\r
+ is_running_ = false;\r
}\r
- else if(!loop_ || av_seek_frame(format_context_.get(), -1, 0, AVSEEK_FLAG_BACKWARD) < 0) // TODO: av_seek_frame does not work for all formats\r
- is_running_ = false;\r
+ }\r
+ catch(...)\r
+ {\r
+ CASPAR_LOG_CURRENT_EXCEPTION();\r
}\r
\r
is_running_ = false;\r
#include "../../../stdafx.h"\r
\r
#include "video_decoder.h"\r
+\r
+#include "../../../../common/exception/exceptions.h"\r
\r
#if defined(_MSC_VER)\r
#pragma warning (push)\r
{\r
implementation(AVCodecContext* codec_context) : codec_context_(codec_context){}\r
\r
- std::shared_ptr<AVFrame> execute(const aligned_buffer& video_packet)\r
+ safe_ptr<AVFrame> execute(const aligned_buffer& video_packet)\r
{ \r
- std::shared_ptr<AVFrame> decoded_frame(avcodec_alloc_frame(), av_free);\r
+ safe_ptr<AVFrame> decoded_frame(avcodec_alloc_frame(), av_free);\r
\r
int frame_finished = 0;\r
const int result = avcodec_decode_video(codec_context_, decoded_frame.get(), &frame_finished, video_packet.data(), video_packet.size());\r
\r
- return result >= 0 ? decoded_frame : nullptr; \r
+ if(result < 0)\r
+ BOOST_THROW_EXCEPTION(invalid_operation() << msg_info("avcodec_decode_video failed"));\r
+\r
+ return decoded_frame; \r
}\r
\r
AVCodecContext* codec_context_;\r
};\r
\r
video_decoder::video_decoder(AVCodecContext* codec_context) : impl_(new implementation(codec_context)){}\r
-std::shared_ptr<AVFrame> video_decoder::execute(const aligned_buffer& video_packet){return impl_->execute(video_packet);}\r
+safe_ptr<AVFrame> video_decoder::execute(const aligned_buffer& video_packet){return impl_->execute(video_packet);}\r
}}}
\ No newline at end of file
{\r
public:\r
video_decoder(AVCodecContext* codec_context);\r
- std::shared_ptr<AVFrame> execute(const aligned_buffer& video_packet);\r
+ safe_ptr<AVFrame> execute(const aligned_buffer& video_packet);\r
private:\r
struct implementation;\r
std::shared_ptr<implementation> impl_;\r
}\r
}\r
\r
- safe_ptr<draw_frame> execute(const std::shared_ptr<AVFrame>& decoded_frame)\r
- { \r
- if(decoded_frame == nullptr)\r
- return draw_frame::eof();\r
- \r
+ safe_ptr<write_frame> execute(const safe_ptr<AVFrame>& decoded_frame)\r
+ { \r
if(sws_context_ == nullptr)\r
{\r
auto write = frame_processor_->create_frame(desc_);\r
};\r
\r
video_transformer::video_transformer(AVCodecContext* codec_context) : impl_(new implementation(codec_context)){}\r
-safe_ptr<draw_frame> video_transformer::execute(const std::shared_ptr<AVFrame>& decoded_frame){return impl_->execute(decoded_frame);}\r
+safe_ptr<write_frame> video_transformer::execute(const safe_ptr<AVFrame>& decoded_frame){return impl_->execute(decoded_frame);}\r
void video_transformer::initialize(const safe_ptr<frame_processor_device>& frame_processor){impl_->initialize(frame_processor); }\r
}}}
\ No newline at end of file
{\r
public:\r
video_transformer(AVCodecContext* codec_context);\r
- safe_ptr<draw_frame> execute(const std::shared_ptr<AVFrame>& video_packet); \r
+ safe_ptr<write_frame> execute(const safe_ptr<AVFrame>& video_packet); \r
void initialize(const safe_ptr<frame_processor_device>& frame_processor);\r
private:\r
struct implementation;\r
return src_frame;\r
\r
double alpha = static_cast<double>(current_frame_)/static_cast<double>(info_.duration);\r
- unsigned char volume = static_cast<unsigned char>(alpha*256.0);\r
\r
auto my_src_frame = transform_frame(src_frame);\r
auto my_dest_frame = transform_frame(dest_frame);\r
\r
- my_src_frame.audio_volume(255-volume);\r
- my_dest_frame.audio_volume(volume);\r
+ my_src_frame.audio_volume(1.0-alpha);\r
+ my_dest_frame.audio_volume(alpha);\r
\r
double dir = info_.direction == transition_direction::from_left ? 1.0 : -1.0; \r
\r
{\r
CASPAR_LOG(debug) << L"Started TBB Worker Thread.";\r
win32_exception::install_handler();\r
- } \r
+ }\r
};\r
\r
int main(int argc, wchar_t* argv[])\r