<ClInclude Include="producer\frame_producer.h" />\r
<ClInclude Include="producer\frame_producer_device.h" />\r
<ClInclude Include="producer\layer.h" />\r
+ <ClInclude Include="producer\separated\separated_producer.h" />\r
<ClInclude Include="producer\transition\transition_producer.h" />\r
<ClInclude Include="video_format.h" />\r
<CustomBuildStep Include="consumers\bluefish\BluefishException.h" />\r
<PrecompiledHeaderFile Condition="'$(Configuration)|$(Platform)'=='Develop|Win32'">../StdAfx.h</PrecompiledHeaderFile>\r
<PreprocessToFile Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">false</PreprocessToFile>\r
</ClCompile>\r
+ <ClCompile Include="producer\separated\separated_producer.cpp">\r
+ <PrecompiledHeaderFile Condition="'$(Configuration)|$(Platform)'=='Profile|Win32'">../../StdAfx.h</PrecompiledHeaderFile>\r
+ <PrecompiledHeaderFile Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">../../StdAfx.h</PrecompiledHeaderFile>\r
+ <PrecompiledHeaderFile Condition="'$(Configuration)|$(Platform)'=='Develop|Win32'">../../StdAfx.h</PrecompiledHeaderFile>\r
+ <PrecompiledHeaderFile Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">../../StdAfx.h</PrecompiledHeaderFile>\r
+ </ClCompile>\r
<ClCompile Include="producer\transition\transition_producer.cpp">\r
<PrecompiledHeaderFile Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">../../StdAfx.h</PrecompiledHeaderFile>\r
<PrecompiledHeaderFile Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">../../StdAfx.h</PrecompiledHeaderFile>\r
<Filter Include="mixer\gpu">\r
<UniqueIdentifier>{2d2ae7ff-03d7-4e8c-a298-fb41ee42a885}</UniqueIdentifier>\r
</Filter>\r
+ <Filter Include="producer\separated">\r
+ <UniqueIdentifier>{cf834e89-32d6-47bc-8d5a-10e032f88e15}</UniqueIdentifier>\r
+ </Filter>\r
</ItemGroup>\r
<ItemGroup>\r
<ClInclude Include="producer\transition\transition_producer.h">\r
<ClInclude Include="mixer\audio\audio_mixer.h">\r
<Filter>mixer\audio</Filter>\r
</ClInclude>\r
+ <ClInclude Include="producer\separated\separated_producer.h">\r
+ <Filter>producer\separated</Filter>\r
+ </ClInclude>\r
</ItemGroup>\r
<ItemGroup>\r
<ClCompile Include="producer\transition\transition_producer.cpp">\r
<ClCompile Include="mixer\audio\audio_mixer.cpp">\r
<Filter>mixer\audio</Filter>\r
</ClCompile>\r
+ <ClCompile Include="producer\separated\separated_producer.cpp">\r
+ <Filter>producer\separated</Filter>\r
+ </ClCompile>\r
</ItemGroup>\r
</Project>
\ No newline at end of file
"uniform sampler2D plane[4]; "\r
"uniform float gain; "\r
"uniform bool HD; "\r
+ "uniform bool has_separate_key; "\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
" 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
+ " if(has_separate_key) "\r
+ " a = texture2D(plane[3], gl_TexCoord[0].st).r+0.2; "\r
" if(HD) "\r
" gl_FragColor = ycbcra_to_bgra_hd(y, cb, cr, a) * gl_Color * gain;"\r
" else "\r
\r
image_kernel::image_kernel() : impl_(new implementation()){}\r
\r
-void image_kernel::apply(const core::pixel_format_desc& pix_desc, const core::image_transform& transform)\r
+void image_kernel::apply(const core::pixel_format_desc& pix_desc, const core::image_transform& transform, bool has_separate_key)\r
{\r
impl_->shaders()[pix_desc.pix_fmt].use();\r
\r
GL(glUniform1f(impl_->shaders()[pix_desc.pix_fmt].get_location("gain"), static_cast<GLfloat>(transform.get_gain())));\r
GL(glUniform1i(impl_->shaders()[pix_desc.pix_fmt].get_location("HD"), pix_desc.planes.at(0).height > 700 ? 1 : 0));\r
+ GL(glUniform1i(impl_->shaders()[pix_desc.pix_fmt].get_location("has_separate_key"), has_separate_key ? 1 : 0));\r
\r
if(transform.get_mode() == core::video_mode::upper)\r
glPolygonStipple(upper_pattern);\r
{\r
public:\r
image_kernel();\r
- void apply(const core::pixel_format_desc& pix_desc, const core::image_transform& mode);\r
+ void apply(const core::pixel_format_desc& pix_desc, const core::image_transform& mode, bool has_seperate_key);\r
\r
private:\r
struct implementation;\r
image_kernel kernel_;\r
\r
safe_ptr<ogl_device> context_;\r
-\r
+ std::shared_ptr<device_buffer> key_;\r
public:\r
implementation(const core::video_format_desc& format_desc) \r
: format_desc_(format_desc)\r
void visit(core::write_frame& frame)\r
{\r
auto gpu_frame = boost::polymorphic_downcast<gpu_write_frame*>(&frame);\r
- auto& desc = gpu_frame->get_pixel_format_desc();\r
- auto& buffers = gpu_frame->get_plane_buffers();\r
+ auto desc = gpu_frame->get_pixel_format_desc();\r
+ auto buffers = gpu_frame->get_plane_buffers();\r
+ auto is_key_frame = gpu_frame->get_image_transform().get_is_key();\r
\r
auto transform = transform_stack_.top();\r
context_->begin_invoke([=]\r
{\r
- GL(glColor4d(1.0, 1.0, 1.0, transform.get_opacity()));\r
- GL(glViewport(0, 0, format_desc_.width, format_desc_.height));\r
- kernel_.apply(desc, transform);\r
-\r
std::vector<safe_ptr<device_buffer>> device_buffers;\r
for(size_t n = 0; n < buffers.size(); ++n)\r
{\r
texture->read(*buffers[n]);\r
device_buffers.push_back(texture);\r
}\r
-\r
- for(size_t n = 0; n < buffers.size(); ++n)\r
+ \r
+ if(is_key_frame) // Its a key_frame just bind the texture for use during the next frame.\r
{\r
- GL(glActiveTexture(GL_TEXTURE0+n));\r
- device_buffers[n]->bind();\r
+ if(!device_buffers.empty()) \r
+ key_ = device_buffers[0]; \r
}\r
-\r
- auto m_p = transform.get_key_translation();\r
- auto m_s = transform.get_key_scale();\r
- double w = static_cast<double>(format_desc_.width);\r
- double h = static_cast<double>(format_desc_.height);\r
+ else\r
+ {\r
+ for(size_t n = 0; n < buffers.size(); ++n)\r
+ {\r
+ GL(glActiveTexture(GL_TEXTURE0+n));\r
+ device_buffers[n]->bind();\r
+ }\r
+ \r
+ if(key_)\r
+ {\r
+ GL(glActiveTexture(GL_TEXTURE0+3));\r
+ key_->bind();\r
+ }\r
+\r
+ GL(glColor4d(1.0, 1.0, 1.0, transform.get_opacity()));\r
+ GL(glViewport(0, 0, format_desc_.width, format_desc_.height));\r
+ kernel_.apply(desc, transform, key_ != nullptr);\r
+ \r
+ auto m_p = transform.get_key_translation();\r
+ auto m_s = transform.get_key_scale();\r
+ double w = static_cast<double>(format_desc_.width);\r
+ double h = static_cast<double>(format_desc_.height);\r
\r
- GL(glEnable(GL_SCISSOR_TEST));\r
- GL(glScissor(static_cast<size_t>(m_p[0]*w), static_cast<size_t>(m_p[1]*h), static_cast<size_t>(m_s[0]*w), static_cast<size_t>(m_s[1]*h)));\r
+ GL(glEnable(GL_SCISSOR_TEST));\r
+ GL(glScissor(static_cast<size_t>(m_p[0]*w), static_cast<size_t>(m_p[1]*h), static_cast<size_t>(m_s[0]*w), static_cast<size_t>(m_s[1]*h)));\r
\r
- auto f_p = transform.get_fill_translation();\r
- auto f_s = transform.get_fill_scale();\r
+ auto f_p = transform.get_fill_translation();\r
+ auto f_s = transform.get_fill_scale();\r
\r
- glBegin(GL_QUADS);\r
- glTexCoord2d(0.0, 0.0); glVertex2d( f_p[0] *2.0-1.0, f_p[1] *2.0-1.0);\r
- glTexCoord2d(1.0, 0.0); glVertex2d((f_p[0]+f_s[0])*2.0-1.0, f_p[1] *2.0-1.0);\r
- glTexCoord2d(1.0, 1.0); glVertex2d((f_p[0]+f_s[0])*2.0-1.0, (f_p[1]+f_s[1])*2.0-1.0);\r
- glTexCoord2d(0.0, 1.0); glVertex2d( f_p[0] *2.0-1.0, (f_p[1]+f_s[1])*2.0-1.0);\r
- glEnd();\r
- GL(glDisable(GL_SCISSOR_TEST));\r
+ glBegin(GL_QUADS);\r
+ glTexCoord2d(0.0, 0.0); glVertex2d( f_p[0] *2.0-1.0, f_p[1] *2.0-1.0);\r
+ glTexCoord2d(1.0, 0.0); glVertex2d((f_p[0]+f_s[0])*2.0-1.0, f_p[1] *2.0-1.0);\r
+ glTexCoord2d(1.0, 1.0); glVertex2d((f_p[0]+f_s[0])*2.0-1.0, (f_p[1]+f_s[1])*2.0-1.0);\r
+ glTexCoord2d(0.0, 1.0); glVertex2d( f_p[0] *2.0-1.0, (f_p[1]+f_s[1])*2.0-1.0);\r
+ glEnd();\r
+ GL(glDisable(GL_SCISSOR_TEST));\r
+ \r
+ key_ = nullptr;\r
+ }\r
});\r
}\r
\r
frames.push_back(my_frame2);\r
return make_safe<basic_frame>(frames);\r
}\r
+\r
+safe_ptr<basic_frame> basic_frame::fill_and_key(const safe_ptr<basic_frame>& fill, const safe_ptr<basic_frame>& key)\r
+{\r
+ std::vector<safe_ptr<basic_frame>> frames;\r
+ key->get_image_transform().set_is_key(true);\r
+ frames.push_back(key);\r
+ frames.push_back(fill);\r
+ return make_safe<basic_frame>(frames);\r
+}\r
\r
}}
\ No newline at end of file
audio_transform& get_audio_transform();\r
\r
static safe_ptr<basic_frame> interlace(const safe_ptr<basic_frame>& frame1, const safe_ptr<basic_frame>& frame2, video_mode::type mode);\r
+ static safe_ptr<basic_frame> fill_and_key(const safe_ptr<basic_frame>& fill, const safe_ptr<basic_frame>& key);\r
\r
static const safe_ptr<basic_frame>& eof()\r
{\r
: opacity_(1.0)\r
, gain_(1.0)\r
, mode_(video_mode::invalid)\r
+ , is_key_(false)\r
{\r
std::fill(fill_translation_.begin(), fill_translation_.end(), 0.0);\r
std::fill(fill_scale_.begin(), fill_scale_.end(), 1.0);\r
if(other.mode_ != video_mode::invalid)\r
mode_ = other.mode_;\r
gain_ *= other.gain_;\r
+ is_key_ = other.is_key_;\r
fill_translation_[0] += other.fill_translation_[0]*fill_scale_[0];\r
fill_translation_[1] += other.fill_translation_[1]*fill_scale_[1];\r
fill_scale_[0] *= other.fill_scale_[0];\r
return image_transform(*this) *= other;\r
}\r
\r
+void image_transform::set_is_key(bool value){is_key_ = value;}\r
+bool image_transform::get_is_key() const{return is_key_;}\r
+\r
image_transform tween(double time, const image_transform& source, const image_transform& dest, double duration, const tweener_t& tweener)\r
{ \r
auto do_tween = [](double time, double source, double dest, double duration, const tweener_t& tweener)\r
\r
image_transform result; \r
result.set_mode(dest.get_mode() != video_mode::invalid ? dest.get_mode() : source.get_mode());\r
+ result.set_is_key(dest.get_is_key());\r
result.set_gain(do_tween(time, source.get_gain(), dest.get_gain(), duration, tweener));\r
result.set_opacity(do_tween(time, source.get_opacity(), dest.get_opacity(), duration, tweener));\r
result.set_fill_translation(do_tween(time, source.get_fill_translation()[0], dest.get_fill_translation()[0], duration, tweener), do_tween(time, source.get_fill_translation()[1], dest.get_fill_translation()[1], duration, tweener));\r
\r
image_transform& operator*=(const image_transform &other);\r
const image_transform operator*(const image_transform &other) const;\r
+\r
+ void set_is_key(bool value);\r
+ bool get_is_key() const;\r
private:\r
double opacity_;\r
double gain_;\r
std::array<double, 2> key_translation_; \r
std::array<double, 2> key_scale_; \r
video_mode::type mode_;\r
+ bool is_key_;\r
};\r
\r
image_transform tween(double time, const image_transform& source, const image_transform& dest, double duration, const tweener_t& tweener);\r
\r
#include "frame_producer.h"\r
#include "color/color_producer.h"\r
+#include "separated/separated_producer.h"\r
\r
#include <common/memory/safe_ptr.h>\r
\r
g_factories.push_back(factory);\r
}\r
\r
-safe_ptr<core::frame_producer> create_producer(const safe_ptr<frame_factory>& my_frame_factory, const std::vector<std::wstring>& params)\r
+safe_ptr<core::frame_producer> do_create_producer(const safe_ptr<frame_factory>& my_frame_factory, const std::vector<std::wstring>& params)\r
{\r
if(params.empty())\r
BOOST_THROW_EXCEPTION(invalid_argument() << arg_name_info("params") << arg_value_info(""));\r
return producer;\r
}\r
\r
+\r
+safe_ptr<core::frame_producer> create_producer(const safe_ptr<frame_factory>& my_frame_factory, const std::vector<std::wstring>& params)\r
+{ \r
+ auto producer = do_create_producer(my_frame_factory, params);\r
+ auto key_producer = frame_producer::empty();\r
+ \r
+ try // to find a key file.\r
+ {\r
+ auto params_copy = params;\r
+ if(params_copy.size() > 0)\r
+ {\r
+ params_copy[0] += L"_A";\r
+ key_producer = do_create_producer(my_frame_factory, params_copy); \r
+ }\r
+ }\r
+ catch(...){}\r
+\r
+ if(key_producer != frame_producer::empty())\r
+ return create_separated_producer(producer, key_producer);\r
+\r
+ return producer;\r
+}\r
+\r
}}
\ No newline at end of file
--- /dev/null
+/*\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
+#include "../../stdafx.h"\r
+\r
+#include "separated_producer.h"\r
+\r
+#include <core/producer/frame/basic_frame.h>\r
+\r
+#include <tbb/parallel_invoke.h>\r
+\r
+namespace caspar { namespace core { \r
+\r
+struct separated_producer : public frame_producer\r
+{ \r
+ safe_ptr<frame_producer> fill_producer_;\r
+ safe_ptr<frame_producer> key_producer;\r
+ \r
+ explicit separated_producer(const safe_ptr<frame_producer>& fill, const safe_ptr<frame_producer>& key) \r
+ : fill_producer_(fill)\r
+ , key_producer(key){}\r
+ \r
+ // frame_producer\r
+ \r
+ virtual safe_ptr<basic_frame> receive()\r
+ {\r
+ auto fill = basic_frame::empty();\r
+ auto key = basic_frame::empty();\r
+ tbb::parallel_invoke\r
+ (\r
+ [&]{fill = receive_and_follow(fill_producer_);},\r
+ [&]{key = receive_and_follow(key_producer);}\r
+ );\r
+\r
+ if(fill == basic_frame::eof())\r
+ return basic_frame::eof();\r
+\r
+ if(key != basic_frame::empty() || key != basic_frame::eof())\r
+ return basic_frame::fill_and_key(fill, key);\r
+\r
+ return fill;\r
+ }\r
+\r
+ virtual std::wstring print() const\r
+ {\r
+ return L"separed";\r
+ } \r
+};\r
+\r
+safe_ptr<frame_producer> create_separated_producer(const safe_ptr<frame_producer>& fill, const safe_ptr<frame_producer>& key)\r
+{\r
+ return make_safe<separated_producer>(fill, key);\r
+}\r
+\r
+}}\r
+\r
--- /dev/null
+/*\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 "../frame_producer.h"\r
+\r
+#include <string>\r
+#include <memory>\r
+\r
+namespace caspar { namespace core {\r
+ \r
+safe_ptr<frame_producer> create_separated_producer(const safe_ptr<frame_producer>& fill, const safe_ptr<frame_producer>& key);\r
+\r
+}}
\ No newline at end of file
frame->audio_data() = std::move(audio_chunk_channel_.front());\r
audio_chunk_channel_.pop_front();\r
}\r
+ else\r
+ frame->get_audio_transform().set_has_audio(false);\r
\r
ouput_channel_.push(make_safe(frame)); \r
} \r
<device>0</device>\r
<stretch>uniform</stretch>\r
<windowed>true</windowed>\r
- </ogl>-->\r
- <!--<audio/>-->\r
+ </ogl>\r
+ <audio/>-->\r
<!--<bluefish>\r
<device>1</device>\r
<embedded-audio>true</embedded-audio>\r