- A start mark has a name and can be started via call() with "play()" as first parameter and the mark label as the second. A jump_to mark will also make the given start mark be played from (makes looping animations possible). Going to a start mark always causes the scene to unpause.
- A stop mark on a frame makes the scene pause until a call("play()", mark_label) occurs.
- When the timeline reaches a jump_to frame the corresponding start mark is seeked to.
- A remove mark removes the entire scene (saving resources after outro).
Special start marks are "intro" which will be the first frame if not defined, and "outro" which will be an empty frame if not defined.
call() with "next()" as first parameter will jump to the next start mark from the current position.
* Created cg_proxy for scene producer going to play mark "intro" on play() and "outro" on stop(). next() jumps to the next start mark from the current frame.
* Implemented interlaced support in scene_producer.
* Implemented support in psd_producer to use the timeline mark features in scene_producer. The marks are stored in the comment track of the psd. A comment on a frame can contain multiple comma separated marks, each with the syntax:
<mark_type> [label]
Examples of complete comment tracks (first number is frame number):
0 "start intro"
20 "stop intro, start outro"
40 "remove"
0 "start intro"
20 "start loop_begin"
80 "jump_to loop_begin, start outro"
100 "remove"
* Fixed bug where usage of chroma key was never reset in the shader.
shader_->set("chroma_blend", params.transform.chroma.threshold, params.transform.chroma.softness);
shader_->set("chroma_spill", params.transform.chroma.spill);
}
+ else
+ shader_->set("chroma", false);
+
// Setup blend_func
std::future<array<const std::uint8_t>> render(const core::video_format_desc& format_desc)
{
+ CASPAR_LOG(info) << layer_stack_.size() << L" " << transform_stack_.size();
return renderer_(std::move(layers_), format_desc);
}
producer/scene/const_producer.cpp
producer/scene/expression_parser.cpp
producer/scene/hotswap_producer.cpp
+ producer/scene/scene_cg_proxy.cpp
producer/scene/scene_producer.cpp
producer/scene/xml_scene_producer.cpp
producer/scene/const_producer.h
producer/scene/expression_parser.h
producer/scene/hotswap_producer.h
+ producer/scene/scene_cg_proxy.h
producer/scene/scene_producer.h
producer/scene/xml_scene_producer.h
#include <boost/lexical_cast.hpp>
#include <common/tweener.h>
+#include <common/except.h>
namespace caspar { namespace core {
auto self = shared_from_this();
if (dependency->depends_on(self))
- throw std::runtime_error("Can't have circular dependencies between bindings");
+ CASPAR_THROW_EXCEPTION(invalid_argument() << msg_info("Can't have circular dependencies between bindings"));
dependency->on_change(self, [=] { evaluate(); });
dependencies_.push_back(dependency);
void set(T value)
{
if (bound())
- {
- throw std::runtime_error("Bound value cannot be set");
- }
+ CASPAR_THROW_EXCEPTION(caspar_exception() << msg_info("Bound value cannot be set"));
if (value == value_)
return;
#include <boost/optional.hpp>
#include <future>
-#include <vector>
+#include <map>
namespace caspar { namespace core {
struct record
{
std::wstring name;
- std::set<std::wstring> file_extensions;
meta_info_extractor info_extractor;
cg_proxy_factory proxy_factory;
cg_producer_factory producer_factory;
struct name {};
struct extension {};
- mutable boost::mutex mutex_;
- std::vector<record> records_;
+ mutable boost::mutex mutex_;
+ std::map<std::wstring, record> records_by_extension_;
public:
void register_cg_producer(
std::wstring cg_producer_name,
{
boost::lock_guard<boost::mutex> lock(mutex_);
- records_.push_back(
+ record rec
{
std::move(cg_producer_name),
- std::move(file_extensions),
std::move(info_extractor),
std::move(proxy_factory),
std::move(producer_factory),
reusable_producer_instance
- });
+ };
+
+ for (auto& extension : file_extensions)
+ {
+ records_by_extension_.insert(std::make_pair(extension, rec));
+ }
}
spl::shared_ptr<frame_producer> create_producer(
boost::lock_guard<boost::mutex> lock(mutex_);
- for (auto& elem : records_)
+ for (auto& elem : records_by_extension_)
{
- if (elem.name == producer_name)
- return elem.proxy_factory(producer);
+ if (elem.second.name == producer_name)
+ return elem.second.proxy_factory(producer);
}
return cg_proxy::empty();
boost::lock_guard<boost::mutex> lock(mutex_);
- for (auto& rec : records_)
+ for (auto& rec : records_by_extension_)
{
- for (auto& file_extension : rec.file_extensions)
- {
- auto p = path(basepath.wstring() + file_extension);
-
- if (exists(p))
- {
- return rec.info_extractor(filename);
- }
- }
+ auto p = path(basepath.wstring() + rec.first);
+ auto found = find_case_insensitive(p.wstring());
+
+ if (found)
+ return rec.second.info_extractor(*found);
}
BOOST_THROW_EXCEPTION(caspar_exception() << msg_info(L"No meta info extractor for " + filename));
{
boost::lock_guard<boost::mutex> lock(mutex_);
- for (auto& rec : records_)
- {
- for (auto& file_extension : rec.file_extensions)
- {
- if (boost::algorithm::iequals(file_extension, extension))
- return true;
- }
- }
-
- return false;
+ return records_by_extension_.find(extension) != records_by_extension_.end();
}
private:
boost::optional<record> find_record(const std::wstring& filename) const
boost::lock_guard<boost::mutex> lock(mutex_);
- for (auto& rec : records_)
+ for (auto& rec : records_by_extension_)
{
- for (auto& file_extension : rec.file_extensions)
- {
- auto p = path(basepath.wstring() + file_extension);
+ auto p = path(basepath.wstring() + rec.first);
- if (find_case_insensitive(p.wstring()))
- return rec;
- }
+ if (find_case_insensitive(p.wstring()))
+ return rec.second;
}
return boost::none;
--- /dev/null
+/*
+* Copyright (c) 2011 Sveriges Television AB <info@casparcg.com>
+*
+* This file is part of CasparCG (www.casparcg.com).
+*
+* CasparCG is free software: you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation, either version 3 of the License, or
+* (at your option) any later version.
+*
+* CasparCG is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with CasparCG. If not, see <http://www.gnu.org/licenses/>.
+*
+* Author: Helge Norberg, helge.norberg@svt.se
+*/
+
+#include "../../StdAfx.h"
+
+#include "scene_cg_proxy.h"
+
+#include <core/producer/frame_producer.h>
+
+#include <boost/property_tree/ptree.hpp>
+#include <boost/property_tree/xml_parser.hpp>
+
+#include <sstream>
+#include <future>
+
+namespace caspar { namespace core { namespace scene {
+
+struct scene_cg_proxy::impl
+{
+ spl::shared_ptr<frame_producer> producer_;
+
+ impl(spl::shared_ptr<frame_producer> producer)
+ : producer_(std::move(producer))
+ {
+ }
+};
+
+scene_cg_proxy::scene_cg_proxy(spl::shared_ptr<frame_producer> producer)
+ : impl_(new impl(std::move(producer)))
+{
+}
+
+void scene_cg_proxy::add(
+ int layer,
+ const std::wstring& template_name,
+ bool play_on_load,
+ const std::wstring& start_from_label,
+ const std::wstring& data)
+{
+ update(layer, data);
+
+ if (play_on_load)
+ play(layer);
+}
+
+void scene_cg_proxy::remove(int layer)
+{
+ impl_->producer_->call({ L"remove()" });
+}
+
+void scene_cg_proxy::play(int layer)
+{
+ impl_->producer_->call({ L"play()", L"intro" });
+}
+
+void scene_cg_proxy::stop(int layer, unsigned int mix_out_duration)
+{
+ impl_->producer_->call({ L"play()", L"outro" });
+}
+
+void scene_cg_proxy::next(int layer)
+{
+ impl_->producer_->call({ L"next()" });
+}
+
+void scene_cg_proxy::update(int layer, const std::wstring& data)
+{
+ if (data.empty())
+ return;
+
+ std::wstringstream stream(data);
+ boost::property_tree::wptree root;
+ boost::property_tree::read_xml(
+ stream,
+ root,
+ boost::property_tree::xml_parser::trim_whitespace | boost::property_tree::xml_parser::no_comments);
+
+ std::vector<std::wstring> parameters;
+
+ for (auto value : root.get_child(L"templateData"))
+ {
+ auto id = value.second.get<std::wstring>(L"<xmlattr>.id");
+ auto val = value.second.get<std::wstring>(L"data.<xmlattr>.value");
+
+ parameters.push_back(std::move(id));
+ parameters.push_back(std::move(val));
+ }
+
+ impl_->producer_->call(parameters);
+}
+
+std::wstring scene_cg_proxy::invoke(int layer, const std::wstring& label)
+{
+ if (boost::ends_with(label, L")"))
+ return impl_->producer_->call({ L"play()", label.substr(0, label.find(L"(")) }).get();
+ else
+ return impl_->producer_->call({ L"play()", label }).get();
+}
+
+std::wstring scene_cg_proxy::description(int layer)
+{
+ return L"<scene producer />";
+}
+
+std::wstring scene_cg_proxy::template_host_info()
+{
+ return L"<scene producer />";
+}
+
+}}}
--- /dev/null
+/*
+* Copyright (c) 2011 Sveriges Television AB <info@casparcg.com>
+*
+* This file is part of CasparCG (www.casparcg.com).
+*
+* CasparCG is free software: you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation, either version 3 of the License, or
+* (at your option) any later version.
+*
+* CasparCG is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with CasparCG. If not, see <http://www.gnu.org/licenses/>.
+*
+* Author: Helge Norberg, helge.norberg@svt.se
+*/
+
+#pragma once
+
+#include <core/producer/cg_proxy.h>
+#include <common/memory.h>
+
+namespace caspar { namespace core { namespace scene {
+
+class scene_cg_proxy : public cg_proxy
+{
+public:
+ scene_cg_proxy(spl::shared_ptr<frame_producer> producer);
+
+ void add(int layer, const std::wstring& template_name, bool play_on_load, const std::wstring& start_from_label, const std::wstring& data) override;
+ void remove(int layer) override;
+ void play(int layer) override;
+ void stop(int layer, unsigned int mix_out_duration) override;
+ void next(int layer) override;
+ void update(int layer, const std::wstring& data) override;
+ std::wstring invoke(int layer, const std::wstring& label) override;
+ std::wstring description(int layer) override;
+ std::wstring template_host_info() override;
+private:
+ struct impl;
+ spl::unique_ptr<impl> impl_;
+};
+
+}}}
#include "../../StdAfx.h"
#include <common/future.h>
+#include <common/prec_timer.h>
+
#include <boost/algorithm/string/split.hpp>
#include <boost/algorithm/string.hpp>
}
};
+mark_action get_mark_action(const std::wstring& name)
+{
+ if (name == L"start")
+ return mark_action::start;
+ else if (name == L"stop")
+ return mark_action::stop;
+ else if (name == L"jump_to")
+ return mark_action::jump_to;
+ else if (name == L"remove")
+ return mark_action::remove;
+ else
+ CASPAR_THROW_EXCEPTION(invalid_argument() << msg_info(L"Invalid mark_action " + name));
+}
+
+struct marker
+{
+ mark_action action;
+ std::wstring label_argument;
+
+ marker(mark_action action, const std::wstring& label_argument)
+ : action(action)
+ , label_argument(label_argument)
+ {
+ }
+};
+
struct scene_producer::impl
{
- constraints pixel_constraints_;
- std::list<layer> layers_;
- interaction_aggregator aggregator_;
- binding<int64_t> frame_number_;
- binding<double> speed_;
- double frame_fraction_;
- std::map<void*, timeline> timelines_;
- std::map<std::wstring, std::shared_ptr<core::variable>> variables_;
- std::vector<std::wstring> variable_names_;
- monitor::subject monitor_subject_;
-
- impl(int width, int height)
+ constraints pixel_constraints_;
+ video_format_desc format_desc_;
+ std::list<layer> layers_;
+ interaction_aggregator aggregator_;
+ binding<int64_t> frame_number_;
+ binding<double> speed_;
+ double frame_fraction_ = 0.0;
+ std::map<void*, timeline> timelines_;
+ std::map<std::wstring, std::shared_ptr<core::variable>> variables_;
+ std::vector<std::wstring> variable_names_;
+ std::multimap<int64_t, marker> markers_by_frame_;
+ monitor::subject monitor_subject_;
+ bool paused_ = true;
+ bool going_to_mark_ = false;
+ draw_frame last_frame_ = draw_frame::empty();
+
+ impl(int width, int height, const video_format_desc& format_desc)
: pixel_constraints_(width, height)
+ , format_desc_(format_desc)
, aggregator_([=] (double x, double y) { return collission_detect(x, y); })
- , frame_fraction_(0)
{
auto speed_variable = std::make_shared<core::variable_impl<double>>(L"1.0", true, 1.0);
store_variable(L"scene_speed", speed_variable);
speed_ = speed_variable->value();
- auto frame_variable = std::make_shared<core::variable_impl<int64_t>>(L"0", true, 0);
+ auto frame_variable = std::make_shared<core::variable_impl<int64_t>>(L"-1", true, -1);
store_variable(L"frame", frame_variable);
frame_number_ = frame_variable->value();
}
variable_names_.push_back(name);
}
+ void add_mark(int64_t frame, mark_action action, const std::wstring& label)
+ {
+ markers_by_frame_.insert(std::make_pair(frame, marker(action, label)));
+ }
+
core::variable& get_variable(const std::wstring& name)
{
auto found = variables_.find(name);
return transform;
}
+ boost::optional<std::pair<int64_t, marker>> find_first_stop_or_jump_or_remove(int64_t start_frame, int64_t end_frame)
+ {
+ auto lower = markers_by_frame_.lower_bound(start_frame);
+ auto upper = markers_by_frame_.upper_bound(end_frame);
+
+ if (lower == markers_by_frame_.end())
+ return boost::none;
+
+ for (auto iter = lower; iter != upper; ++iter)
+ {
+ auto action = iter->second.action;
+
+ if (action == mark_action::stop || action == mark_action::jump_to || action == mark_action::remove)
+ return *iter;
+ }
+
+ return boost::none;
+ }
+
+ boost::optional<std::pair<int64_t, marker>> find_first_start(int64_t start_frame)
+ {
+ auto lower = markers_by_frame_.lower_bound(start_frame);
+
+ if (lower == markers_by_frame_.end())
+ return boost::none;
+
+ for (auto iter = lower; iter != markers_by_frame_.end(); ++iter)
+ {
+ auto action = iter->second.action;
+
+ if (action == mark_action::start)
+ return *iter;
+ }
+
+ return boost::none;
+ }
+
draw_frame render_frame()
{
+ if (format_desc_.field_count == 1)
+ return render_progressive_frame();
+ else
+ {
+ prec_timer timer;
+ timer.tick_millis(0);
+
+ auto field1 = render_progressive_frame();
+
+ timer.tick(0.5 / format_desc_.fps);
+
+ auto field2 = render_progressive_frame();
+
+ return draw_frame::interlace(field1, field2, format_desc_.field_mode);
+ }
+ }
+
+ draw_frame render_progressive_frame()
+ {
+ if (paused_)
+ return last_frame_;
+
+ frame_fraction_ += speed_.get();
+
+ if (std::abs(frame_fraction_) >= 1.0)
+ {
+ int64_t delta = static_cast<int64_t>(frame_fraction_);
+ auto previous_frame = frame_number_.get();
+ auto next_frame = frame_number_.get() + delta;
+ auto marker = find_first_stop_or_jump_or_remove(previous_frame + 1, next_frame);
+
+ if (marker && marker->second.action == mark_action::remove)
+ {
+ remove();
+ return last_frame_;
+ }
+ if (marker && !going_to_mark_)
+ {
+ if (marker->second.action == mark_action::stop)
+ {
+ frame_number_.set(marker->first);
+ frame_fraction_ = 0.0;
+ paused_ = true;
+ }
+ else if (marker->second.action == mark_action::jump_to)
+ {
+ go_to_marker(marker->second.label_argument, 0);
+ }
+ }
+ else
+ {
+ frame_number_.set(next_frame);
+ frame_fraction_ -= delta;
+ }
+
+ going_to_mark_ = false;
+ }
+
for (auto& timeline : timelines_)
timeline.second.on_frame(frame_number_.get());
frames.push_back(frame);
}
- frame_fraction_ += speed_.get();
-
- if (std::abs(frame_fraction_) >= 1.0)
- {
- int64_t delta = static_cast<int64_t>(frame_fraction_);
- frame_number_.set(frame_number_.get() + delta);
- frame_fraction_ -= delta;
- }
-
- return draw_frame(frames);
+ return last_frame_ = draw_frame(frames);
}
void on_interaction(const interaction_event::ptr& event)
}
std::future<std::wstring> call(const std::vector<std::wstring>& params)
+ {
+ if (!params.empty() && boost::ends_with(params.at(0), L"()"))
+ return make_ready_future(handle_call(params));
+ else
+ return make_ready_future(handle_variable_set(params));
+ }
+
+ std::wstring handle_variable_set(const std::vector<std::wstring>& params)
{
for (int i = 0; i + 1 < params.size(); i += 2)
{
found->second->from_string(params[i + 1]);
}
- return make_ready_future(std::wstring(L""));
+ return L"";
+ }
+
+ std::wstring handle_call(const std::vector<std::wstring>& params)
+ {
+ auto call = params.at(0);
+
+ if (call == L"play()")
+ go_to_marker(params.at(1), -1);
+ else if (call == L"remove()")
+ remove();
+ else if (call == L"next()")
+ next();
+ else
+ CASPAR_THROW_EXCEPTION(caspar_exception() << msg_info(L"Unknown call " + call));
+
+ return L"";
+ }
+
+ void remove()
+ {
+ paused_ = true;
+ last_frame_ = draw_frame::empty();
+ layers_.clear();
+ }
+
+ void next()
+ {
+ auto marker = find_first_start(frame_number_.get() + 1);
+
+ if (marker)
+ {
+ frame_number_.set(marker->first - 1);
+ frame_fraction_ = 0.0;
+ paused_ = false;
+ going_to_mark_ = true;
+ }
+ else
+ {
+ remove();
+ }
+ }
+
+ void go_to_marker(const std::wstring& marker_name, int64_t offset)
+ {
+ for (auto& marker : markers_by_frame_)
+ {
+ if (marker.second.label_argument == marker_name && marker.second.action == mark_action::start)
+ {
+ frame_number_.set(marker.first + offset);
+ frame_fraction_ = 0.0;
+ paused_ = false;
+ going_to_mark_ = true;
+
+ return;
+ }
+ }
+
+ if (marker_name == L"intro")
+ {
+ frame_number_.set(offset);
+ frame_fraction_ = 0.0;
+ paused_ = false;
+ going_to_mark_ = true;
+ }
+ else if (marker_name == L"outro")
+ {
+ remove();
+ }
+ else
+ CASPAR_LOG(info) << print() << L" no marker called " << marker_name << " found";
}
std::wstring print() const
}
};
-scene_producer::scene_producer(int width, int height)
- : impl_(new impl(width, height))
+scene_producer::scene_producer(int width, int height, const video_format_desc& format_desc)
+ : impl_(new impl(width, height, format_desc))
{
}
impl_->store_variable(name, var);
}
+void scene_producer::add_mark(int64_t frame, mark_action action, const std::wstring& label)
+{
+ impl_->add_mark(frame, action, label);
+}
+
core::variable& scene_producer::get_variable(const std::wstring& name)
{
return impl_->get_variable(name);
if (params.size() < 1 || !boost::iequals(params.at(0), L"[SCENE]"))
return core::frame_producer::empty();
- auto scene = spl::make_shared<scene_producer>(format_desc.width, format_desc.height);
+ auto scene = spl::make_shared<scene_producer>(format_desc.width, format_desc.height, format_desc);
text::text_info text_info;
text_info.font = L"ArialMT";
}
};
+enum class mark_action
+{
+ start,
+ stop,
+ jump_to,
+ remove
+};
+
+mark_action get_mark_action(const std::wstring& name);
+
class scene_producer : public frame_producer_base
{
public:
- scene_producer(int width, int height);
+ scene_producer(int width, int height, const video_format_desc& format_desc);
~scene_producer();
class draw_frame receive_impl() override;
bool collides(double x, double y) const override;
std::wstring print() const override;
std::wstring name() const override;
- std::future<std::wstring> call(const std::vector<std::wstring>& params) override;
+ std::future<std::wstring> call(const std::vector<std::wstring>& params) override;
boost::property_tree::wptree info() const override;
monitor::subject& monitor_output();
store_keyframe(to_affect.identity(), k);
}
+ void add_mark(int64_t frame, mark_action action, const std::wstring& label);
+
core::variable& get_variable(const std::wstring& name) override;
const std::vector<std::wstring>& get_variables() const override;
private:
#include <future>
#include "scene_producer.h"
+#include "scene_cg_proxy.h"
namespace caspar { namespace core { namespace scene {
}
}
+void init(module_dependencies dependencies)
+{
+ dependencies.cg_registry->register_cg_producer(
+ L"scene",
+ { L".scene" },
+ [](const std::wstring& filename) { return ""; },
+ [](const spl::shared_ptr<core::frame_producer>& producer)
+ {
+ return spl::make_shared<core::scene::scene_cg_proxy>(producer);
+ },
+ [](
+ const spl::shared_ptr<core::frame_factory>& factory,
+ const core::video_format_desc& format_desc,
+ const std::wstring& filename)
+ {
+ return create_xml_scene_producer(factory, format_desc, { filename });
+ },
+ false);
+}
+
spl::shared_ptr<core::frame_producer> create_xml_scene_producer(
const spl::shared_ptr<core::frame_factory>& frame_factory,
const core::video_format_desc& format_desc,
if (params.empty())
return core::frame_producer::empty();
- std::wstring filename = env::media_folder() + L"/" + params[0] + L".xml";
+ std::wstring filename = env::template_folder() + L"/" + params[0] + L".scene";
if (!boost::filesystem::is_regular_file(boost::filesystem::path(filename)))
return core::frame_producer::empty();
int width = root.get<int>(L"scene.<xmlattr>.width");
int height = root.get<int>(L"scene.<xmlattr>.height");
- auto scene = spl::make_shared<scene_producer>(width, height);
+ auto scene = spl::make_shared<scene_producer>(width, height, format_desc);
for (auto elem : root.get_child(L"scene.variables"))
{
}
}
+ if (root.get_child_optional(L"scene.marks"))
+ {
+ for (auto& mark : root.get_child(L"scene.marks"))
+ {
+ auto at = mark.second.get<int64_t>(L"<xmlattr>.at");
+ auto type = get_mark_action(mark.second.get<std::wstring>(L"<xmlattr>.type"));
+ auto label = mark.second.get(L"<xmlattr>.label", L"");
+
+ scene->add_mark(at, type, label);
+ }
+ }
+
for (auto& elem : root.get_child(L"scene.timelines"))
{
auto& variable = scene->get_variable(elem.second.get<std::wstring>(L"<xmlattr>.variable"));
#include <common/memory.h>
-namespace caspar { namespace core {
+#include "../../module_dependencies.h"
-class frame_producer;
-class frame_factory;
-struct video_format_desc;
-
-namespace scene {
+namespace caspar { namespace core { namespace scene {
+void init(module_dependencies dependencies);
spl::shared_ptr<core::frame_producer> create_xml_scene_producer(const spl::shared_ptr<core::frame_factory>& frame_factory, const core::video_format_desc& format_desc, const std::vector<std::wstring>& params);
}}}
FT_Face face;
if (FT_New_Face(lib_.get(), u8(info.font_file).c_str(), 0, &face))
- CASPAR_THROW_EXCEPTION(freetype_exception() << msg_info("Failed to load font"));
+ CASPAR_THROW_EXCEPTION(freetype_exception() << msg_info(L"Failed to load font " + info.font));
face_.reset(face, [](FT_Face ptr) { FT_Done_Face(ptr); });
#include "descriptor.h"
#include "misc.h"
+#include <common/log.h>
+
#include <boost/property_tree/ptree.hpp>
+#include <boost/property_tree/xml_parser.hpp>
#include <memory>
namespace caspar { namespace psd {
+ std::wstring debug_ptree(const boost::property_tree::wptree& tree)
+ {
+ std::wstringstream str;
+ boost::property_tree::xml_writer_settings<std::wstring> w(' ', 3);
+ boost::property_tree::write_xml(str, tree, w);
+ str.flush();
+ return str.str();
+ }
+
class descriptor::context::scoped_holder
{
descriptor::context::ptr_type ctx_;
}
};
- descriptor::descriptor() : context_(std::make_shared<context>())
+ descriptor::descriptor(const std::wstring& debug_name) : context_(std::make_shared<context>(debug_name))
{
context_->stack.push_back(&context_->root);
}
std::wstring key = stream.read_id_string();
read_value(key, stream);
}
+
+ if (context_->stack.size() == 1)
+ CASPAR_LOG(trace) << context_->debug_name << L":\n\n" << debug_ptree(context_->root) << L"\n";
}
void descriptor::read_value(const std::wstring& key, bigendian_file_input_stream& stream)
Ptree root;
std::vector<Ptree *> stack;
+ std::wstring debug_name;
friend descriptor;
friend class scoped_holder;
class scoped_holder;
+
+ context(const std::wstring& debug_name)
+ : debug_name(debug_name)
+ {
+ }
};
friend class context::scoped_holder;
explicit descriptor(const std::wstring& key, context::ptr_type context);
public:
- descriptor();
+ descriptor(const std::wstring& debug_name);
~descriptor();
void populate(bigendian_file_input_stream& stream);
if(stream.read_long() != 16) //"descriptor version" should be 16
CASPAR_THROW_EXCEPTION(psd_file_format_exception() << msg_info("descriptor version should be 16"));
- descriptor solid_descriptor;
+ descriptor solid_descriptor(L"solid_color");
solid_descriptor.populate(stream);
solid_color_.red = static_cast<unsigned char>(solid_descriptor.items().get(L"Clr .Rd ", 0.0) + 0.5);
solid_color_.green = static_cast<unsigned char>(solid_descriptor.items().get(L"Clr .Grn ", 0.0) + 0.5);
if(stream.read_long() != 16) //"descriptor version" should be 16
CASPAR_THROW_EXCEPTION(psd_file_format_exception() << msg_info("descriptor version should be 16"));
- descriptor timeline_descriptor;
+ descriptor timeline_descriptor(L"timeline");
timeline_descriptor.populate(stream);
timeline_info_.swap(timeline_descriptor.items());
}
if(stream.read_long() != 16) //"descriptor version" should be 16
CASPAR_THROW_EXCEPTION(psd_file_format_exception() << msg_info("Invalid descriptor version while reading text-data"));
- descriptor text_descriptor;
+ descriptor text_descriptor(L"text");
text_descriptor.populate(stream);
auto text_info = text_descriptor.items().get_optional<std::wstring>(L"EngineData");
if(text_info.is_initialized())
if(stream.read_long() != 16) //"descriptor version" should be 16
CASPAR_THROW_EXCEPTION(psd_file_format_exception() << msg_info("Invalid descriptor version while reading text warp-data"));
- descriptor warp_descriptor;
+ descriptor warp_descriptor(L"warp");
warp_descriptor.populate(stream);
stream.read_double(); // w_top
stream.read_double(); // w_left
case 1075: //timeline information
{
input_.read_long(); //descriptor version, should be 16
- descriptor timeline_descriptor;
+ descriptor timeline_descriptor(L"global timeline");
timeline_descriptor.populate(input_);
- timeline_desc_.swap(timeline_descriptor.items());
+
+ if (timeline_desc_.empty())
+ timeline_desc_.swap(timeline_descriptor.items());
}
break;
#include <core/producer/frame_producer.h>
#include <core/producer/color/color_producer.h>
#include <core/producer/text/text_producer.h>
+#include <core/producer/scene/scene_cg_proxy.h>
#include <core/producer/scene/scene_producer.h>
#include <core/producer/scene/const_producer.h>
#include <core/producer/scene/hotswap_producer.h>
return result;
}
+
class layer_link_constructor
{
struct linked_layer_record
node.get<int>(L"numerator"), node.get<int>(L"denominator"));
}
+void create_marks_from_comments(
+ const spl::shared_ptr<core::scene::scene_producer>& scene,
+ const core::video_format_desc& format_desc,
+ const boost::property_tree::wptree& comment_track)
+{
+ auto keylist = comment_track.get_child_optional(L"keyList");
+
+ if (!keylist)
+ return;
+
+ for (auto& key : *keylist)
+ {
+ auto time = get_rational(key.second.get_child(L"time"));
+ auto frame = get_frame_number(format_desc, time);
+ auto text = key.second.get<std::wstring>(L"animKey.Vl ");
+ std::vector<std::wstring> marks;
+ boost::split(marks, text, boost::is_any_of(","));
+
+ for (auto& mark : marks)
+ {
+ boost::trim_if(mark, [](wchar_t c) { return isspace(c) || c == 0; });
+
+ std::vector<std::wstring> args;
+ boost::split(args, mark, boost::is_any_of(" \t"), boost::algorithm::token_compress_on);
+
+ scene->add_mark(frame, core::scene::get_mark_action(args.at(0)), args.size() == 1 ? L"" : args.at(1));
+ }
+ }
+}
+
+void create_marks(
+ const spl::shared_ptr<core::scene::scene_producer>& scene,
+ const core::video_format_desc& format_desc,
+ const boost::property_tree::wptree& global_timeline)
+{
+ auto time = get_rational(global_timeline.get_child(L"duration"));
+ auto remove_at_frame = get_frame_number(format_desc, time);
+
+ scene->add_mark(remove_at_frame, core::scene::mark_action::remove, L"");
+
+ auto tracklist = global_timeline.get_child_optional(L"globalTrackList");
+
+ if (!tracklist)
+ return;
+
+ for (auto& track : *tracklist)
+ {
+ auto track_id = track.second.get<std::wstring>(L"stdTrackID");
+
+ if (track_id == L"globalCommentTrack")
+ create_marks_from_comments(scene, format_desc, track.second);
+ }
+}
+
void create_timelines(
const spl::shared_ptr<core::scene::scene_producer>& scene,
const core::video_format_desc& format_desc,
spl::shared_ptr<core::frame_producer> create_psd_scene_producer(const spl::shared_ptr<core::frame_factory>& frame_factory, const core::video_format_desc& format_desc, const std::vector<std::wstring>& params)
{
- std::wstring filename = env::media_folder() + params[0] + L".psd";
+ std::wstring filename = env::template_folder() + params[0] + L".psd";
auto found_file = find_case_insensitive(filename);
if (!found_file)
psd_document doc;
doc.parse(*found_file);
- spl::shared_ptr<core::scene::scene_producer> root(spl::make_shared<core::scene::scene_producer>(doc.width(), doc.height()));
+ spl::shared_ptr<core::scene::scene_producer> root(spl::make_shared<core::scene::scene_producer>(doc.width(), doc.height(), format_desc));
layer_link_constructor link_constructor;
for (auto& text_layer : text_producers_by_layer_name)
text_layer.second->text().bind(root->create_variable<std::wstring>(boost::to_lower_copy(text_layer.first), true, L""));
+ if (doc.has_timeline())
+ create_marks(root, format_desc, doc.timeline());
+
auto params2 = params;
params2.erase(params2.begin());
return false;
});
+ dependencies.cg_registry->register_cg_producer(
+ L"scene",
+ { L".psd" },
+ [](const std::wstring& filename) { return ""; },
+ [](const spl::shared_ptr<core::frame_producer>& producer)
+ {
+ return spl::make_shared<core::scene::scene_cg_proxy>(producer);
+ },
+ [](
+ const spl::shared_ptr<core::frame_factory>& factory,
+ const core::video_format_desc& format_desc,
+ const std::wstring& filename)
+ {
+ return create_psd_scene_producer(factory, format_desc, { filename });
+ },
+ false);
}
}}
std::wstringstream str;
boost::property_tree::xml_writer_settings<std::wstring> w(' ', 3);
boost::property_tree::write_xml(str, env::properties(), w);
- CASPAR_LOG(info) << L"casparcg.config:\n-----------------------------------------\n" << str.str().c_str() << L"-----------------------------------------";
+ CASPAR_LOG(info) << L"casparcg.config:\n-----------------------------------------\n" << str.str() << L"-----------------------------------------";
caspar_server.start();
core::diagnostics::osd::register_sink();
diag_subject_->attach_parent(monitor_subject_);
- initialize_modules(module_dependencies(
- system_info_provider_repo_,
- cg_registry_,
- media_info_repo_));
+ module_dependencies dependencies(
+ system_info_provider_repo_,
+ cg_registry_,
+ media_info_repo_);
+ initialize_modules(dependencies);
core::text::init();
-
- register_producer_factory(&core::scene::create_dummy_scene_producer);
- register_producer_factory(&core::scene::create_xml_scene_producer);
+ core::scene::init(dependencies);
}
void start()