transform_stack_.pop();\r
}\r
\r
+ float get_master_volume() const\r
+ {\r
+ return master_volume_;\r
+ }\r
+\r
void set_master_volume(float volume)\r
{\r
master_volume_ = volume;\r
void audio_mixer::begin(core::basic_frame& frame){impl_->begin(frame);}\r
void audio_mixer::visit(core::write_frame& frame){impl_->visit(frame);}\r
void audio_mixer::end(){impl_->end();}\r
+float audio_mixer::get_master_volume() const { return impl_->get_master_volume(); }\r
void audio_mixer::set_master_volume(float volume) { impl_->set_master_volume(volume); }\r
audio_buffer audio_mixer::operator()(const video_format_desc& format_desc, const channel_layout& layout){return impl_->mix(format_desc, layout);}\r
\r
virtual void visit(core::write_frame& frame);\r
virtual void end();\r
\r
+ float get_master_volume() const;\r
void set_master_volume(float volume);\r
\r
audio_buffer operator()(const video_format_desc& format_desc, const channel_layout& layout);\r
return blend_mode::normal;\r
}\r
\r
+std::wstring get_blend_mode(blend_mode::type mode)\r
+{\r
+ switch (mode)\r
+ {\r
+ case blend_mode::normal:\r
+ return L"normal";\r
+ case blend_mode::lighten:\r
+ return L"lighten";\r
+ case blend_mode::darken:\r
+ return L"darken";\r
+ case blend_mode::multiply:\r
+ return L"multiply";\r
+ case blend_mode::average:\r
+ return L"average";\r
+ case blend_mode::add:\r
+ return L"add";\r
+ case blend_mode::subtract:\r
+ return L"subtract";\r
+ case blend_mode::difference:\r
+ return L"difference";\r
+ case blend_mode::negation:\r
+ return L"negation";\r
+ case blend_mode::exclusion:\r
+ return L"exclusion";\r
+ case blend_mode::screen:\r
+ return L"screen";\r
+ case blend_mode::overlay:\r
+ return L"overlay";\r
+ case blend_mode::soft_light:\r
+ return L"soft_light";\r
+ case blend_mode::hard_light:\r
+ return L"hard_light";\r
+ case blend_mode::color_dodge:\r
+ return L"color_dodge";\r
+ case blend_mode::color_burn:\r
+ return L"color_burn";\r
+ case blend_mode::linear_dodge:\r
+ return L"linear_dodge";\r
+ case blend_mode::linear_burn:\r
+ return L"linear_burn";\r
+ case blend_mode::linear_light:\r
+ return L"linear_light";\r
+ case blend_mode::vivid_light:\r
+ return L"vivid_ligh";\r
+ case blend_mode::pin_light:\r
+ return L"pin_light";\r
+ case blend_mode::hard_mix:\r
+ return L"hard_mix";\r
+ case blend_mode::reflect:\r
+ return L"reflect";\r
+ case blend_mode::glow:\r
+ return L"glow";\r
+ case blend_mode::phoenix:\r
+ return L"phoenix";\r
+ case blend_mode::contrast:\r
+ return L"contrast";\r
+ case blend_mode::saturation:\r
+ return L"saturation";\r
+ case blend_mode::color:\r
+ return L"color";\r
+ case blend_mode::luminosity:\r
+ return L"luminosity";\r
+ default:\r
+ return L"normal";\r
+ }\r
+}\r
+\r
chroma::type get_chroma_mode(const std::wstring& str)\r
{\r
- if (boost::iequals(str, L"none"))\r
+ if (boost::iequals(str, L"none"))\r
return chroma::none;\r
else if(boost::iequals(str, L"red"))\r
return chroma::red;\r
return chroma::none;\r
}\r
\r
+std::wstring get_chroma_mode(chroma::type type)\r
+{\r
+ switch (type)\r
+ {\r
+ case chroma::none:\r
+ return L"none";\r
+ case chroma::red:\r
+ return L"red";\r
+ case chroma::yellow:\r
+ return L"yellow";\r
+ case chroma::green:\r
+ return L"green";\r
+ case chroma::torquise:\r
+ return L"torquise";\r
+ case chroma::blue:\r
+ return L"blue";\r
+ case chroma::magenta:\r
+ return L"magenta";\r
+ default:\r
+ return L"none";\r
+ }\r
+}\r
+\r
}}\r
magenta = 0xff00ff\r
};\r
\r
- unsigned int key;\r
- float threshold,\r
- softness,\r
- spill,\r
- blur;\r
- bool show_mask;\r
+ type key;\r
+ float threshold;\r
+ float softness;\r
+ float spill;\r
+ float blur;\r
+ bool show_mask;\r
\r
- chroma(type m=none)\r
- : key(m), threshold(0.0), softness(0.0), spill(0.0),\r
- blur(0.0), show_mask(false) {}\r
+ chroma(type m = none)\r
+ : key(m)\r
+ , threshold(0.0)\r
+ , softness(0.0)\r
+ , spill(0.0)\r
+ , blur(0.0)\r
+ , show_mask(false)\r
+ {\r
+ }\r
};\r
\r
struct blend_mode\r
};\r
\r
blend_mode::type get_blend_mode(const std::wstring& str);\r
+std::wstring get_blend_mode(blend_mode::type mode);\r
\r
chroma::type get_chroma_mode(const std::wstring& str);\r
+std::wstring get_chroma_mode(chroma::type type);\r
\r
}}\r
{ \r
return make_safe<write_frame>(ogl_, tag, desc, audio_channel_layout);\r
}\r
+\r
+ blend_mode::type get_blend_mode(int index)\r
+ {\r
+ return executor_.invoke([=]\r
+ {\r
+ return blend_modes_[index].mode;\r
+ });\r
+ }\r
\r
void set_blend_mode(int index, blend_mode::type value)\r
{\r
}, high_priority);\r
}\r
\r
+ chroma get_chroma(int index)\r
+ {\r
+ return executor_.invoke([=]\r
+ {\r
+ return blend_modes_[index].chroma;\r
+ });\r
+ }\r
+\r
void set_chroma(int index, const chroma & value)\r
{\r
executor_.begin_invoke([=]\r
}, high_priority);\r
}\r
\r
+ float get_master_volume()\r
+ {\r
+ return executor_.invoke([=]\r
+ {\r
+ return audio_mixer_.get_master_volume();\r
+ });\r
+ }\r
+\r
void set_master_volume(float volume)\r
{\r
executor_.begin_invoke([=]\r
void mixer::send(const std::pair<std::map<int, safe_ptr<core::basic_frame>>, std::shared_ptr<void>>& frames){ impl_->send(frames);}\r
core::video_format_desc mixer::get_video_format_desc() const { return impl_->get_video_format_desc(); }\r
safe_ptr<core::write_frame> mixer::create_frame(const void* tag, const core::pixel_format_desc& desc, const channel_layout& audio_channel_layout){ return impl_->create_frame(tag, desc, audio_channel_layout); } \r
+blend_mode::type mixer::get_blend_mode(int index) { return impl_->get_blend_mode(index); }\r
void mixer::set_blend_mode(int index, blend_mode::type value){impl_->set_blend_mode(index, value);}\r
+chroma mixer::get_chroma(int index) { return impl_->get_chroma(index); }\r
void mixer::set_chroma(int index, const chroma & value){impl_->set_chroma(index, value);}\r
void mixer::clear_blend_mode(int index) { impl_->clear_blend_mode(index); }\r
void mixer::clear_blend_modes() { impl_->clear_blend_modes(); }\r
+float mixer::get_master_volume() { return impl_->get_master_volume(); }\r
void mixer::set_master_volume(float volume) { impl_->set_master_volume(volume); }\r
void mixer::set_video_format_desc(const video_format_desc& format_desc){impl_->set_video_format_desc(format_desc);}\r
boost::unique_future<boost::property_tree::wptree> mixer::info() const{return impl_->info();}\r
core::video_format_desc get_video_format_desc() const; // nothrow\r
void set_video_format_desc(const video_format_desc& format_desc);\r
\r
+ blend_mode::type get_blend_mode(int index);\r
void set_blend_mode(int index, blend_mode::type value);\r
- void set_chroma(int index, const chroma & value);\r
+ chroma get_chroma(int index);\r
+ void set_chroma(int index, const chroma& value);\r
void clear_blend_mode(int index);\r
void clear_blend_modes();\r
\r
+ float get_master_volume();\r
void set_master_volume(float volume);\r
\r
boost::unique_future<boost::property_tree::wptree> info() const;\r
transforms_.clear();\r
}, high_priority);\r
}\r
+\r
+ frame_transform get_current_transform(int index)\r
+ {\r
+ return executor_.invoke([=]\r
+ {\r
+ return transforms_[index].fetch();\r
+ });\r
+ }\r
\r
layer& get_layer(int index)\r
{\r
void stage::apply_transform(int index, const std::function<core::frame_transform(core::frame_transform)>& transform, unsigned int mix_duration, const std::wstring& tween){impl_->apply_transform(index, transform, mix_duration, tween);}\r
void stage::clear_transforms(int index){impl_->clear_transforms(index);}\r
void stage::clear_transforms(){impl_->clear_transforms();}\r
+frame_transform stage::get_current_transform(int index) { return impl_->get_current_transform(index); }\r
void stage::spawn_token(){impl_->spawn_token();}\r
void stage::load(int index, const safe_ptr<frame_producer>& producer, bool preview, int auto_play_delta){impl_->load(index, producer, preview, auto_play_delta);}\r
void stage::pause(int index){impl_->pause(index);}\r
void apply_transform(int index, const transform_func_t& transform, unsigned int mix_duration = 0, const std::wstring& tween = L"linear");\r
void clear_transforms(int index);\r
void clear_transforms();\r
+ frame_transform get_current_transform(int index);\r
\r
void spawn_token();\r
\r
// UGLY HACK\r
tbb::concurrent_unordered_map<int, std::vector<stage::transform_tuple_t>> deferred_transforms;\r
\r
+core::frame_transform MixerCommand::get_current_transform()\r
+{\r
+ return GetChannel()->stage()->get_current_transform(GetLayerIndex());\r
+}\r
+\r
bool MixerCommand::DoExecute()\r
-{ \r
+{\r
+ using boost::lexical_cast;\r
//Perform loading of the clip\r
try\r
{ \r
\r
if(_parameters[0] == L"KEYER" || _parameters[0] == L"IS_KEY")\r
{\r
+ if (_parameters.size() == 1)\r
+ return reply_value([](const frame_transform& t) { return t.is_key ? 1 : 0; });\r
+\r
bool value = boost::lexical_cast<int>(_parameters.at(1));\r
transforms.push_back(stage::transform_tuple_t(GetLayerIndex(), [=](frame_transform transform) -> frame_transform\r
{\r
}\r
else if(_parameters[0] == L"OPACITY")\r
{\r
+ if (_parameters.size() == 1)\r
+ return reply_value([](const frame_transform& t) { return t.opacity; });\r
+\r
int duration = _parameters.size() > 2 ? boost::lexical_cast<int>(_parameters[2]) : 0;\r
std::wstring tween = _parameters.size() > 3 ? _parameters[3] : L"linear";\r
\r
}\r
else if(_parameters[0] == L"FILL" || _parameters[0] == L"FILL_RECT")\r
{\r
+ if (_parameters.size() == 1)\r
+ {\r
+ auto transform = get_current_transform();\r
+ auto translation = transform.fill_translation;\r
+ auto scale = transform.fill_scale;\r
+ SetReplyString(\r
+ L"201 MIXER OK\r\n" \r
+ + lexical_cast<std::wstring>(translation[0]) + L" "\r
+ + lexical_cast<std::wstring>(translation[1]) + L" "\r
+ + lexical_cast<std::wstring>(scale[0]) + L" "\r
+ + lexical_cast<std::wstring>(scale[1]) + L"\r\n");\r
+ return true;\r
+ }\r
+\r
int duration = _parameters.size() > 5 ? boost::lexical_cast<int>(_parameters[5]) : 0;\r
std::wstring tween = _parameters.size() > 6 ? _parameters[6] : L"linear";\r
double x = boost::lexical_cast<double>(_parameters.at(1));\r
}\r
else if(_parameters[0] == L"CLIP" || _parameters[0] == L"CLIP_RECT")\r
{\r
+ if (_parameters.size() == 1)\r
+ {\r
+ auto transform = get_current_transform();\r
+ auto translation = transform.clip_translation;\r
+ auto scale = transform.clip_scale;\r
+ SetReplyString(\r
+ L"201 MIXER OK\r\n" \r
+ + lexical_cast<std::wstring>(translation[0]) + L" "\r
+ + lexical_cast<std::wstring>(translation[1]) + L" "\r
+ + lexical_cast<std::wstring>(scale[0]) + L" "\r
+ + lexical_cast<std::wstring>(scale[1]) + L"\r\n");\r
+ return true;\r
+ }\r
+\r
int duration = _parameters.size() > 5 ? boost::lexical_cast<int>(_parameters[5]) : 0;\r
std::wstring tween = _parameters.size() > 6 ? _parameters[6] : L"linear";\r
double x = boost::lexical_cast<double>(_parameters.at(1));\r
}\r
else if(_parameters[0] == L"BLEND")\r
{\r
+ if (_parameters.size() == 1)\r
+ {\r
+ auto blend_mode = GetChannel()->mixer()->get_blend_mode(GetLayerIndex());\r
+ SetReplyString(L"201 MIXER OK\r\n" \r
+ + lexical_cast<std::wstring>(get_blend_mode(blend_mode)) \r
+ + L"\r\n");\r
+ return true;\r
+ }\r
+\r
auto blend_str = _parameters.at(1); \r
int layer = GetLayerIndex();\r
blend_mode::type && blend = get_blend_mode(blend_str);\r
}\r
else if(_parameters[0] == L"CHROMA")\r
{\r
- int layer = GetLayerIndex();\r
+ if (_parameters.size() == 1)\r
+ {\r
+ auto chroma = GetChannel()->mixer()->get_chroma(GetLayerIndex());\r
+ SetReplyString(L"201 MIXER OK\r\n" \r
+ + get_chroma_mode(chroma.key)\r
+ + (chroma.key == chroma::none\r
+ ? L""\r
+ : L" "\r
+ + lexical_cast<std::wstring>(chroma.threshold) + L" "\r
+ + lexical_cast<std::wstring>(chroma.softness))\r
+ + L"\r\n");\r
+ // Add the rest when they are actually used and documented\r
+ return true;\r
+ }\r
+\r
+ int layer = GetLayerIndex();\r
chroma chroma;\r
- chroma.key = get_chroma_mode(_parameters[1]);\r
- chroma.threshold = boost::lexical_cast<double>(_parameters[2]);\r
- chroma.softness = boost::lexical_cast<double>(_parameters[3]);\r
- chroma.spill = _parameters.size() > 4 ? boost::lexical_cast<double>(_parameters[4]) : 0.0f;\r
- chroma.blur = _parameters.size() > 5 ? boost::lexical_cast<double>(_parameters[5]) : 0.0f;\r
- chroma.show_mask = _parameters.size() > 6 ? bool(boost::lexical_cast<int>(_parameters[6])) : false;\r
+ chroma.key = get_chroma_mode(_parameters[1]);\r
+\r
+ if (chroma.key != chroma::none)\r
+ {\r
+ chroma.threshold = boost::lexical_cast<double>(_parameters[2]);\r
+ chroma.softness = boost::lexical_cast<double>(_parameters[3]);\r
+ chroma.spill = _parameters.size() > 4 ? boost::lexical_cast<double>(_parameters[4]) : 0.0f;\r
+ chroma.blur = _parameters.size() > 5 ? boost::lexical_cast<double>(_parameters[5]) : 0.0f;\r
+ chroma.show_mask = _parameters.size() > 6 ? bool(boost::lexical_cast<int>(_parameters[6])) : false;\r
+ }\r
+\r
GetChannel()->mixer()->set_chroma(GetLayerIndex(), chroma);\r
}\r
else if(_parameters[0] == L"MASTERVOLUME")\r
{\r
+ if (_parameters.size() == 1)\r
+ {\r
+ auto volume = GetChannel()->mixer()->get_master_volume();\r
+ SetReplyString(L"201 MIXER OK\r\n" \r
+ + lexical_cast<std::wstring>(volume) + L"\r\n");\r
+ return true;\r
+ }\r
+\r
float master_volume = boost::lexical_cast<float>(_parameters.at(1));\r
GetChannel()->mixer()->set_master_volume(master_volume);\r
}\r
else if(_parameters[0] == L"BRIGHTNESS")\r
{\r
+ if (_parameters.size() == 1)\r
+ return reply_value([](const frame_transform& t) { return t.brightness; });\r
+\r
auto value = boost::lexical_cast<double>(_parameters.at(1));\r
int duration = _parameters.size() > 2 ? boost::lexical_cast<int>(_parameters[2]) : 0;\r
std::wstring tween = _parameters.size() > 3 ? _parameters[3] : L"linear";\r
}\r
else if(_parameters[0] == L"SATURATION")\r
{\r
+ if (_parameters.size() == 1)\r
+ return reply_value([](const frame_transform& t) { return t.saturation; });\r
+\r
auto value = boost::lexical_cast<double>(_parameters.at(1));\r
int duration = _parameters.size() > 2 ? boost::lexical_cast<int>(_parameters[2]) : 0;\r
std::wstring tween = _parameters.size() > 3 ? _parameters[3] : L"linear";\r
}\r
else if(_parameters[0] == L"CONTRAST")\r
{\r
+ if (_parameters.size() == 1)\r
+ return reply_value([](const frame_transform& t) { return t.contrast; });\r
+\r
auto value = boost::lexical_cast<double>(_parameters.at(1));\r
int duration = _parameters.size() > 2 ? boost::lexical_cast<int>(_parameters[2]) : 0;\r
std::wstring tween = _parameters.size() > 3 ? _parameters[3] : L"linear";\r
}\r
else if(_parameters[0] == L"LEVELS")\r
{\r
+ if (_parameters.size() == 1)\r
+ {\r
+ auto levels = get_current_transform().levels;\r
+ SetReplyString(L"201 MIXER OK\r\n"\r
+ + lexical_cast<std::wstring>(levels.min_input) + L" "\r
+ + lexical_cast<std::wstring>(levels.max_input) + L" "\r
+ + lexical_cast<std::wstring>(levels.gamma) + L" "\r
+ + lexical_cast<std::wstring>(levels.min_output) + L" "\r
+ + lexical_cast<std::wstring>(levels.max_output) + L"\r\n");\r
+ return true;\r
+ }\r
+\r
levels value;\r
value.min_input = boost::lexical_cast<double>(_parameters.at(1));\r
value.max_input = boost::lexical_cast<double>(_parameters.at(2));\r
}\r
else if(_parameters[0] == L"VOLUME")\r
{\r
+ if (_parameters.size() == 1)\r
+ return reply_value([](const frame_transform& t) { return t.volume; });\r
+\r
int duration = _parameters.size() > 2 ? boost::lexical_cast<int>(_parameters[2]) : 0;\r
std::wstring tween = _parameters.size() > 3 ? _parameters[3] : L"linear";\r
double value = boost::lexical_cast<double>(_parameters[1]);\r
\r
#include "AMCPCommand.h"\r
\r
-namespace caspar { namespace protocol {\r
+namespace caspar {\r
+namespace core {\r
+ struct frame_transform;\r
+}\r
+\r
+namespace protocol {\r
\r
std::wstring ListMedia();\r
std::wstring ListTemplates();\r
class MixerCommand : public AMCPCommandBase<true, AddToQueue, 1>\r
{\r
std::wstring print() const { return L"MixerCommand";}\r
+ core::frame_transform get_current_transform();\r
+ template<typename Func>\r
+ bool reply_value(const Func& extractor)\r
+ {\r
+ auto value = extractor(get_current_transform());\r
+\r
+ SetReplyString(L"201 MIXER OK\r\n"\r
+ + boost::lexical_cast<std::wstring>(value) + L"\r\n");\r
+\r
+ return true;\r
+ }\r
bool DoExecute();\r
};\r
\r