From 62c7da66319a85de98331bcf9dded7490ab62884 Mon Sep 17 00:00:00 2001 From: Helge Norberg Date: Mon, 20 Feb 2017 19:29:15 +0100 Subject: [PATCH 1/1] [scene_producer] Added possibility to CALL/CG PLAY/CG STOP/CG NEXT/CG INVOKE layers in a scene either at a specific timeline frame or whenever any bool expression becomes true --- core/producer/scene/scene.xsd | 61 +++++++++++++++++++- core/producer/scene/scene_producer.cpp | 43 +++++++++++++- core/producer/scene/scene_producer.h | 4 +- core/producer/scene/xml_scene_producer.cpp | 67 +++++++++++++++++++++- 4 files changed, 169 insertions(+), 6 deletions(-) diff --git a/core/producer/scene/scene.xsd b/core/producer/scene/scene.xsd index e4a722097..4fcffe0bb 100644 --- a/core/producer/scene/scene.xsd +++ b/core/producer/scene/scene.xsd @@ -1,7 +1,7 @@ - + @@ -224,7 +224,64 @@ - + + + + Tasks are scheduled to happen whenever a certain condition arises. + Use the at attribute to let the condition be that the timeline arrives at a specific frame + or the when attribute to specify a custom bool expression that when becomes true triggers the task. + + + + + + Performs the equivalent of an AMCP CALL on the specified layer. + + + See the producer documentation for the possible arguments. + + Trigger the CALL when the timeline gets to this specific frame. + Trigger the CALL when the specified bool expression becomes true. + The producer on this layer will receive the CALL. + + + + Performs the equivalent of a CG PLAY on the specified layer. + + Trigger the CG PLAY when the timeline gets to this specific frame. + Trigger the CG PLAY when the specified bool expression becomes true. + The CG producer on this layer will receive the CG PLAY. + + + + Performs the equivalent of a CG STOP on the specified layer. + + Trigger the CG STOP when the timeline gets to this specific frame. + Trigger the CG STOP when the specified bool expression becomes true. + The CG producer on this layer will receive the CG STOP. + + + + Performs the equivalent of a CG NEXT on the specified layer. + + Trigger the CG NEXT when the timeline gets to this specific frame. + Trigger the CG NEXT when the specified bool expression becomes true. + The CG producer on this layer will receive the CG NEXT. + + + + Performs the equivalent of a CG INVOKE on the specified layer with the specified label. + + Trigger the CG INVOKE when the timeline gets to this specific frame. + Trigger the CG INVOKE when the specified bool expression becomes true. + The CG producer on this layer will receive the CG INVOKE. + The label that will be invoked. + + + + + + diff --git a/core/producer/scene/scene_producer.cpp b/core/producer/scene/scene_producer.cpp index 7c3466b11..efcfa3516 100644 --- a/core/producer/scene/scene_producer.cpp +++ b/core/producer/scene/scene_producer.cpp @@ -199,6 +199,15 @@ struct scene_producer::impl layers_.reverse(); } + layer& get_layer(const std::wstring& name) + { + for (auto& layer : layers_) + if (layer.name.get() == name) + return layer; + + CASPAR_THROW_EXCEPTION(user_error() << msg_info(name + L" not found in scene")); + } + void store_keyframe(void* timeline_identity, const keyframe& k) { timelines_[timeline_identity].keyframes.insert(std::make_pair(k.destination_frame, k)); @@ -216,6 +225,27 @@ struct scene_producer::impl markers_by_frame_.insert(std::make_pair(frame, marker(action, label))); } + void add_task(binding when, std::function task) + { + auto subscription = when.on_change([=] + { + if (when.get()) + { + try + { + task(); + } + catch (...) + { + CASPAR_LOG_CURRENT_EXCEPTION_AT_LEVEL(debug); + CASPAR_LOG(error) << print() << " Error when invoking scene task. Turn on log level debug for stacktrace."; + } + } + }); + + task_subscriptions_.push_back(std::move(subscription)); + } + core::variable& get_variable(const std::wstring& name) { auto found = variables_.find(name); @@ -614,10 +644,16 @@ layer& scene_producer::create_layer( return impl_->create_layer(producer, 0, 0, name); } -void scene_producer::reverse_layers() { +void scene_producer::reverse_layers() +{ impl_->reverse_layers(); } +layer& scene_producer::get_layer(const std::wstring& name) +{ + return impl_->get_layer(name); +} + binding scene_producer::timeline_frame() { return impl_->timeline_frame(); @@ -681,6 +717,11 @@ void scene_producer::add_mark(int64_t frame, mark_action action, const std::wstr impl_->add_mark(frame, action, label); } +void scene_producer::add_task(binding when, std::function task) +{ + impl_->add_task(std::move(when), std::move(task)); +} + core::variable& scene_producer::get_variable(const std::wstring& name) { return impl_->get_variable(name); diff --git a/core/producer/scene/scene_producer.h b/core/producer/scene/scene_producer.h index 4ae0b86c5..a7c37710c 100644 --- a/core/producer/scene/scene_producer.h +++ b/core/producer/scene/scene_producer.h @@ -133,6 +133,7 @@ public: layer& create_layer( const spl::shared_ptr& producer, const std::wstring& name); void reverse_layers(); + layer& get_layer(const std::wstring& name); binding timeline_frame(); binding speed(); @@ -225,7 +226,8 @@ public: store_keyframe(to_affect.identity(), k); } - void add_mark(int64_t frame, mark_action action, const std::wstring& label); + void add_mark(int64_t at_frame, mark_action action, const std::wstring& label); + void add_task(binding when, std::function task); core::variable& get_variable(const std::wstring& name) override; const std::vector& get_variables() const override; diff --git a/core/producer/scene/xml_scene_producer.cpp b/core/producer/scene/xml_scene_producer.cpp index 3c1bc18ca..10c4aca6d 100644 --- a/core/producer/scene/xml_scene_producer.cpp +++ b/core/producer/scene/xml_scene_producer.cpp @@ -38,6 +38,7 @@ #include #include +#include #include "scene_producer.h" #include "scene_cg_proxy.h" @@ -236,8 +237,8 @@ spl::shared_ptr create_xml_scene_producer( ptree_verify_element_name(k, L"keyframe"); - auto easing = k.second.get(L".easing", L""); - auto at = ptree_get(k.second, L".at"); + auto easing = k.second.get(L".easing", L""); + auto at = ptree_get(k.second, L".at"); auto keyframe_variable_name = L"timeline." + variable_name + L"." + boost::lexical_cast(at); @@ -254,6 +255,68 @@ spl::shared_ptr create_xml_scene_producer( return scene->get_variable(name); }; + int task_id = 0; + if (root.get_child_optional(L"scene.tasks")) + { + for (auto& task : root | witerate_children(L"scene.tasks") | welement_context_iteration) + { + auto& element_name = task.first; + + std::vector call_params; + + if (element_name == L"call") + { + for (auto& arg : task.second) + { + if (arg.first == L"") + continue; + + ptree_verify_element_name(arg, L"arg"); + call_params.push_back(ptree_get_value(arg.second)); + } + } + else if (element_name == L"cg_play") + call_params.push_back(L"play()"); + else if (element_name == L"cg_stop") + call_params.push_back(L"stop()"); + else if (element_name == L"cg_next") + call_params.push_back(L"next()"); + else if (element_name == L"cg_invoke") + { + call_params.push_back(L"invoke()"); + call_params.push_back(ptree_get(task.second, L".label")); + } + else + CASPAR_THROW_EXCEPTION(user_error() << msg_info(L"Only valid element names in are call, cg_play, cg_stop, cg_next and cg_invoke")); + + auto at_frame = task.second.get_optional(L".at"); + auto when = task.second.get_optional(L".when"); + auto to_layer = ptree_get(task.second, L".to_layer"); + auto producer = scene->get_layer(to_layer).producer.get(); + + binding condition; + + if (at_frame) + condition = scene->timeline_frame() == *at_frame; + else if (when) + condition = scene->create_variable(L"tasks.task_" + boost::lexical_cast(task_id), false, *when); + else + CASPAR_THROW_EXCEPTION(user_error() << msg_info(L"Task elements must have either an at attribute or a when attribute")); + + auto weak_producer = static_cast>(producer); + + scene->add_task(std::move(condition), [=] + { + auto strong = weak_producer.lock(); + + if (strong) + strong->call(call_params); + }); + + ++task_id; + } + } + for (auto& var_name : scene->get_variables()) { CASPAR_SCOPED_CONTEXT_MSG(L"/scene/variables/variable[@id=" + var_name + L"]/text()"); -- 2.39.2