**********************************************************************************\r
\r
----------------------------------------------------------------------------------\r
-2.7.1. MIXER VIDEO IS_KEY\r
+2.7.1. MIXER VIDEO KEY_DEPTH\r
----------------------------------------------------------------------------------\r
Syntax: \r
\r
- MIXER [channel:int]{-[layer:int]|-0} VIDEO IS_KEY {is_key:0,1|0}\r
+ MIXER [channel:int]{-[layer:int]|-0} VIDEO KEY_DEPTH [key_depth:uint]\r
\r
Description: \r
\r
- If "is_key" equals 1 then the specified layer will not be rendered, \r
- instead it will be used as the key for the layer above.\r
+ If "key_depth" is greater than 0 then the specified layer will not be rendered, \r
+ instead it will be used as the key for the "key_depth" number of layers above.\r
\r
Examples: \r
\r
- >> MIXER VIDEO 1-0 IS_KEY 1\r
+ >> MIXER VIDEO 1-0 KEY_DEPTH 2\r
\r
Note:\r
\r
\r
image_kernel kernel_;\r
\r
- std::shared_ptr<device_buffer> key_;\r
+ struct keyer\r
+ {\r
+ std::shared_ptr<device_buffer> key;\r
+ size_t depth;\r
+ } keyer_;\r
+\r
public:\r
implementation(const core::video_format_desc& format_desc) \r
: format_desc_(format_desc)\r
\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 gpu_frame = dynamic_cast<gpu_write_frame*>(&frame);\r
+ if(!gpu_frame)\r
+ return;\r
+\r
+ auto desc = gpu_frame->get_pixel_format_desc();\r
+ auto buffers = gpu_frame->get_plane_buffers();\r
+ auto transform = transform_stack_.top();\r
+ \r
+ if(buffers.empty()) \r
+ return;\r
\r
- auto transform = transform_stack_.top();\r
ogl_device::begin_invoke([=]\r
- {\r
- std::vector<safe_ptr<device_buffer>> device_buffers;\r
- for(size_t n = 0; n < buffers.size(); ++n)\r
+ { \r
+ if(transform.get_key_depth() > 0) // Its a key_frame just save buffer for next frame.\r
{\r
- auto texture = ogl_device::create_device_buffer(desc.planes[n].width, desc.planes[n].height, desc.planes[n].channels);\r
- texture->read(*buffers[n]);\r
- device_buffers.push_back(texture);\r
- }\r
- \r
- if(transform.get_is_key()) // Its a key_frame just save buffer for next frame.\r
- {\r
- if(!device_buffers.empty()) \r
- key_ = device_buffers[0]; \r
+ auto texture = create_device_buffer(desc.planes[0]);\r
+ texture->read(*buffers[0]);\r
+ keyer_.key = texture;\r
+ keyer_.depth = transform.get_key_depth(); \r
}\r
else\r
- {\r
- for(size_t n = 0; n < buffers.size(); ++n)\r
+ { \r
+ const bool has_separate_key = keyer_.depth > 0;\r
+ const size_t max_index = has_separate_key ? 3 : 4;\r
+ \r
+ std::vector<safe_ptr<device_buffer>> device_buffers;\r
+ for(size_t n = 0; n < std::min(max_index, buffers.size()); ++n)\r
+ {\r
+ auto texture = create_device_buffer(desc.planes[n]);\r
+ texture->read(*buffers[n]);\r
+ device_buffers.push_back(texture);\r
+ } \r
+\r
+ if(has_separate_key && keyer_.key)\r
+ device_buffers.push_back(make_safe(keyer_.key));\r
+\r
+ for(size_t n = 0; n < device_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
+ kernel_.apply(desc, transform, has_separate_key);\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
+ auto w = static_cast<double>(format_desc_.width);\r
+ auto 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
glEnd();\r
GL(glDisable(GL_SCISSOR_TEST)); \r
\r
- key_ = nullptr; \r
+ if(keyer_.depth == 0)\r
+ keyer_.key.reset();\r
+ else\r
+ --keyer_.depth;\r
}\r
});\r
}\r
{\r
return ogl_device::begin_invoke([=]() -> safe_ptr<const host_buffer>\r
{\r
+ keyer_.depth = 0;\r
reading_->map();\r
render_targets_[0]->attach(0);\r
GL(glClear(GL_COLOR_BUFFER_BIT));\r
std::rotate(render_targets_.begin(), render_targets_.begin() + 1, render_targets_.end());\r
});\r
}\r
+\r
+ safe_ptr<device_buffer> create_device_buffer(const core::pixel_format_desc::plane& plane)\r
+ {\r
+ return ogl_device::create_device_buffer(plane.width, plane.height, plane.channels);\r
+ }\r
\r
std::vector<safe_ptr<host_buffer>> create_buffers(const core::pixel_format_desc& format)\r
{\r
return fill;\r
\r
std::vector<safe_ptr<basic_frame>> frames;\r
- key->get_image_transform().set_is_key(true);\r
+ key->get_image_transform().set_key_depth(1);\r
frames.push_back(key);\r
frames.push_back(fill);\r
return basic_frame(std::move(frames));\r
: opacity_(1.0)\r
, gain_(1.0)\r
, mode_(video_mode::invalid)\r
- , is_key_(false)\r
+ , key_depth_(0)\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
+ key_depth_ = std::max(key_depth_, other.key_depth_);\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
+void image_transform::set_key_depth(size_t value){key_depth_ = value;}\r
+size_t image_transform::get_key_depth() const {return key_depth_;}\r
\r
image_transform tween(double time, const image_transform& source, const image_transform& dest, double duration, const tweener_t& tweener)\r
{ \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(source.get_is_key() | dest.get_is_key());\r
+ result.set_key_depth(std::max(source.get_key_depth(), dest.get_key_depth()));\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
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
+ void set_key_depth(size_t depth);\r
+ size_t get_key_depth() 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
+ size_t key_depth_;\r
};\r
\r
image_transform tween(double time, const image_transform& source, const image_transform& dest, double duration, const tweener_t& tweener);\r
{ \r
if(_parameters[0] == L"VIDEO")\r
{\r
- if(_parameters[1] == L"IS_KEY")\r
+ if(_parameters[1] == L"KEY_DEPTH")\r
{\r
- bool value = lexical_cast_or_default(_parameters.at(2), false);\r
+ size_t value = lexical_cast_or_default(_parameters.at(2), 0);\r
auto transform = [=](image_transform transform) -> image_transform\r
{\r
- transform.set_is_key(value);\r
+ transform.set_key_depth(value);\r
return transform; \r
};\r
\r
}\r
else if(_parameters[1] == L"OPACITY")\r
{\r
- int duration = _parameters.size() > 2 ? lexical_cast_or_default(_parameters[3], 0) : 0;\r
- std::wstring tween = _parameters.size() > 3 ? _parameters[4] : L"linear";\r
-\r
double value = boost::lexical_cast<double>(_parameters.at(2));\r
+ int duration = _parameters.size() > 3 ? lexical_cast_or_default(_parameters[3], 0) : 0;\r
+ std::wstring tween = _parameters.size() > 4 ? _parameters[4] : L"linear";\r
\r
auto transform = [=](image_transform transform) -> image_transform\r
{\r
}\r
else if(_parameters[1] == L"GAIN")\r
{\r
- int duration = _parameters.size() > 2 ? lexical_cast_or_default(_parameters[3], 0) : 0;\r
- std::wstring tween = _parameters.size() > 3 ? _parameters[4] : L"linear";\r
double value = boost::lexical_cast<double>(_parameters.at(2));\r
+ int duration = _parameters.size() > 3 ? lexical_cast_or_default(_parameters[3], 0) : 0;\r
+ std::wstring tween = _parameters.size() > 4 ? _parameters[4] : L"linear";\r
\r
auto transform = [=](image_transform transform) -> image_transform\r
{\r
}\r
else if(_parameters[1] == L"FILL_RECT")\r
{\r
- int duration = _parameters.size() > 5 ? lexical_cast_or_default(_parameters[6], 0) : 0;\r
- std::wstring tween = _parameters.size() > 6 ? _parameters[7] : L"linear";\r
double x = boost::lexical_cast<double>(_parameters.at(2));\r
double y = boost::lexical_cast<double>(_parameters.at(3));\r
double x_s = boost::lexical_cast<double>(_parameters.at(4));\r
double y_s = boost::lexical_cast<double>(_parameters.at(5));\r
+ int duration = _parameters.size() > 5 ? lexical_cast_or_default(_parameters[6], 0) : 0;\r
+ std::wstring tween = _parameters.size() > 6 ? _parameters[7] : L"linear";\r
\r
auto transform = [=](image_transform transform) -> image_transform\r
{\r
}\r
else if(_parameters[1] == L"KEY_RECT")\r
{\r
- int duration = _parameters.size() > 5 ? lexical_cast_or_default(_parameters[6], 0) : 0;\r
- std::wstring tween = _parameters.size() > 6 ? _parameters[7] : L"linear";\r
double x = boost::lexical_cast<double>(_parameters.at(2));\r
double y = boost::lexical_cast<double>(_parameters.at(3));\r
double x_s = boost::lexical_cast<double>(_parameters.at(4));\r
double y_s = boost::lexical_cast<double>(_parameters.at(5));\r
+ int duration = _parameters.size() > 6 ? lexical_cast_or_default(_parameters[6], 0) : 0;\r
+ std::wstring tween = _parameters.size() > 7 ? _parameters[7] : L"linear";\r
\r
auto transform = [=](image_transform transform) -> image_transform\r
{\r
}\r
else if(_parameters[1] == L"GRID")\r
{\r
- int duration = _parameters.size() > 2 ? lexical_cast_or_default(_parameters[3], 0) : 0;\r
- std::wstring tween = _parameters.size() > 3 ? _parameters[4] : L"linear";\r
int n = boost::lexical_cast<int>(_parameters.at(2));\r
+ int duration = _parameters.size() > 3 ? lexical_cast_or_default(_parameters[3], 0) : 0;\r
+ std::wstring tween = _parameters.size() > 4 ? _parameters[4] : L"linear";\r
+\r
double delta = 1.0/static_cast<double>(n);\r
for(int x = 0; x < n; ++x)\r
{\r
}\r
else if(_parameters[1] == L"RESET")\r
{\r
- int duration = _parameters.size() > 1 ? lexical_cast_or_default(_parameters[2], 0) : 0;\r
- std::wstring tween = _parameters.size() > 2 ? _parameters[3] : L"linear";\r
+ int duration = _parameters.size() > 2 ? lexical_cast_or_default(_parameters[2], 0) : 0;\r
+ std::wstring tween = _parameters.size() > 3 ? _parameters[3] : L"linear";\r
\r
int layer = GetLayerIndex(std::numeric_limits<int>::min());\r
if(layer != std::numeric_limits<int>::min())\r
{\r
if(_parameters[1] == L"GAIN")\r
{\r
- int duration = _parameters.size() > 2 ? lexical_cast_or_default(_parameters[3], 0) : 0;\r
- std::wstring tween = _parameters.size() > 3 ? _parameters[4] : L"linear";\r
- double value = boost::lexical_cast<double>(_parameters[2]);\r
+ double value = boost::lexical_cast<double>(_parameters.at(2));\r
+ int duration = _parameters.size() > 3 ? lexical_cast_or_default(_parameters[3], 0) : 0;\r
+ std::wstring tween = _parameters.size() > 4 ? _parameters[4] : L"linear";\r
\r
auto transform = [=](audio_transform transform) -> audio_transform\r
{\r
}\r
else if(_parameters[1] == L"RESET")\r
{\r
- int duration = _parameters.size() > 1 ? lexical_cast_or_default(_parameters[2], 0) : 0;\r
- std::wstring tween = _parameters.size() > 2 ? _parameters[3] : L"linear";\r
+ int duration = _parameters.size() > 2 ? lexical_cast_or_default(_parameters[2], 0) : 0;\r
+ std::wstring tween = _parameters.size() > 3 ? _parameters[3] : L"linear";\r
GetChannel()->mixer()->reset_audio_transform(duration, tween);\r
}\r
}\r
else if(_parameters[0] == L"RESET")\r
{\r
- int duration = _parameters.size() > 1 ? lexical_cast_or_default(_parameters[2], 0) : 0;\r
- std::wstring tween = _parameters.size() > 2 ? _parameters[3] : L"linear";\r
+ int duration = _parameters.size() > 1 ? lexical_cast_or_default(_parameters[1], 0) : 0;\r
+ std::wstring tween = _parameters.size() > 2 ? _parameters[2] : L"linear";\r
GetChannel()->mixer()->reset_image_transform(duration, tween);\r
GetChannel()->mixer()->reset_audio_transform(duration, tween);\r
}\r
<embedded-audio>true</embedded-audio>\r
<latency>low</latency>\r
<key>external</key>\r
- <output>key_only</output>\r
+ <output>fill_and_key</output>\r
</decklink>\r
<!--<ogl>\r
<device>1</device>\r