]> git.sesse.net Git - casparcg/commitdiff
* Implemented supports for timeline marks in scene_producer. A mark is set on a speci...
authorHelge Norberg <helge.norberg@svt.se>
Fri, 12 Jun 2015 12:51:46 +0000 (14:51 +0200)
committerHelge Norberg <helge.norberg@svt.se>
Fri, 12 Jun 2015 12:51:46 +0000 (14:51 +0200)
  - 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.

19 files changed:
accelerator/ogl/image/image_kernel.cpp
accelerator/ogl/image/image_mixer.cpp
core/CMakeLists.txt
core/producer/binding.h
core/producer/cg_proxy.cpp
core/producer/scene/scene_cg_proxy.cpp [new file with mode: 0644]
core/producer/scene/scene_cg_proxy.h [new file with mode: 0644]
core/producer/scene/scene_producer.cpp
core/producer/scene/scene_producer.h
core/producer/scene/xml_scene_producer.cpp
core/producer/scene/xml_scene_producer.h
core/producer/text/utils/texture_font.cpp
modules/psd/descriptor.cpp
modules/psd/descriptor.h
modules/psd/layer.cpp
modules/psd/psd_document.cpp
modules/psd/psd_scene_producer.cpp
shell/main.cpp
shell/server.cpp

index 0bb2643c7ec47b2a48917bbd325f172c6456a141..4c7b6a9ec565f8b8b99d818aa4584a3771ce6838 100644 (file)
@@ -268,6 +268,9 @@ struct image_kernel::impl
                        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
                
index bea3bbe6b059fbd2c86863a3d85430c937290459..43a898d9d1750849d31cbcf5207ddb08a25674fa 100644 (file)
@@ -356,6 +356,7 @@ public:
        
        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);
        }
        
index 6eeeef4e5dee72098ea98d14ff4adfde4acc4010..e37d094f13d479f75d095c37968eaef452199d5e 100644 (file)
@@ -30,6 +30,7 @@ set(SOURCES
                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
 
@@ -89,6 +90,7 @@ set(HEADERS
                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
 
index 77e4261cb080774f2ca5cb1cea3c63bb33850359..20196fb344da632c5c2c4969c1d18d8c26e749a1 100644 (file)
@@ -33,6 +33,7 @@
 #include <boost/lexical_cast.hpp>
 
 #include <common/tweener.h>
+#include <common/except.h>
 
 namespace caspar { namespace core {
 
@@ -56,7 +57,7 @@ struct impl_base : std::enable_shared_from_this<impl_base>
                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);
@@ -129,9 +130,7 @@ private:
                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;
index 8faa0a3b6d5732d5ce01164895bfe0ef40354be8..b9a9b71b6d83e24c562f00838b82605ea9418a24 100644 (file)
@@ -38,7 +38,7 @@
 #include <boost/optional.hpp>
 
 #include <future>
-#include <vector>
+#include <map>
 
 namespace caspar { namespace core {
 
@@ -69,7 +69,6 @@ private:
        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;
@@ -79,8 +78,8 @@ private:
        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,
@@ -92,15 +91,19 @@ public:
        {
                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(
@@ -124,10 +127,10 @@ public:
 
                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();
@@ -183,17 +186,13 @@ public:
 
                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));
@@ -203,16 +202,7 @@ public:
        {
                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
@@ -223,15 +213,12 @@ private:
 
                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;
diff --git a/core/producer/scene/scene_cg_proxy.cpp b/core/producer/scene/scene_cg_proxy.cpp
new file mode 100644 (file)
index 0000000..a741aae
--- /dev/null
@@ -0,0 +1,128 @@
+/*
+* 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 />";
+}
+
+}}}
diff --git a/core/producer/scene/scene_cg_proxy.h b/core/producer/scene/scene_cg_proxy.h
new file mode 100644 (file)
index 0000000..527372c
--- /dev/null
@@ -0,0 +1,48 @@
+/*
+* 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_;
+};
+
+}}}
index b58b6c33eba0d159167d5b9d10ce9f827f72ba19..d949556dcbdce145848563197d744a7cbbf624cf 100644 (file)
@@ -22,6 +22,8 @@
 #include "../../StdAfx.h"
 
 #include <common/future.h>
+#include <common/prec_timer.h>
+
 #include <boost/algorithm/string/split.hpp>
 #include <boost/algorithm/string.hpp>
 
@@ -88,28 +90,59 @@ struct timeline
        }
 };
 
+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();
        }
@@ -139,6 +172,11 @@ struct scene_producer::impl
                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);
@@ -208,8 +246,103 @@ struct scene_producer::impl
                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());
 
@@ -225,16 +358,7 @@ struct scene_producer::impl
                        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)
@@ -271,6 +395,14 @@ struct scene_producer::impl
        }
 
        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)
                {
@@ -280,7 +412,77 @@ struct scene_producer::impl
                                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
@@ -306,8 +508,8 @@ struct scene_producer::impl
        }
 };
 
-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))
 {
 }
 
@@ -385,6 +587,11 @@ void scene_producer::store_variable(
        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);
@@ -400,7 +607,7 @@ spl::shared_ptr<core::frame_producer> create_dummy_scene_producer(const spl::sha
        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";
index f08a550de000c49b2383db104e970b83b084d6fc..533091cd4d362dfc1687c576c81d0bd2d7a5b451 100644 (file)
@@ -101,10 +101,20 @@ public:
        }
 };
 
+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;
@@ -113,7 +123,7 @@ public:
        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();
 
@@ -212,6 +222,8 @@ public:
                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:
index 7475fafc3adde4d95fd8358f5babcc6e324d2435..b876e6d619eca026be970e8a2dccc60f5466f0e8 100644 (file)
@@ -35,6 +35,7 @@
 #include <future>
 
 #include "scene_producer.h"
+#include "scene_cg_proxy.h"
 
 namespace caspar { namespace core { namespace scene {
 
@@ -60,6 +61,26 @@ void deduce_expression(variable& var, const variable_repository& repo)
        }
 }
 
+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,
@@ -68,7 +89,7 @@ spl::shared_ptr<core::frame_producer> create_xml_scene_producer(
        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();
@@ -84,7 +105,7 @@ spl::shared_ptr<core::frame_producer> create_xml_scene_producer(
        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"))
        {
@@ -156,6 +177,18 @@ spl::shared_ptr<core::frame_producer> create_xml_scene_producer(
                }
        }
 
+       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"));
index 0a2b07b51dd26317ea8855f7e9b873169b6c4030..effb39e8ef33004e306db03cb6cff6a3073c7d3c 100644 (file)
 
 #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);
 
 }}}
index 19629527e82a5980f87602b979f797c5651a8eb8..5a81e30e31c275adf2e72b15096e659b984b614c 100644 (file)
@@ -58,7 +58,7 @@ public:
                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); });
 
index c678a22764cfd582174804a90bf0ec48223fa17d..a91f36c6be93db89739c65531ccd59887b141264 100644 (file)
 #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_;
@@ -44,7 +56,7 @@ namespace caspar { namespace psd {
                }
        };
 
-       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);
        }
@@ -69,6 +81,9 @@ void descriptor::populate(bigendian_file_input_stream& stream)
                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)
index 3db2a764d55617372d5b08c48ba3f394439cfd7b..c48963787f99edb6f6f9e95b588053496aa713bb 100644 (file)
@@ -55,10 +55,16 @@ class descriptor
 
                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;
 
@@ -67,7 +73,7 @@ class descriptor
        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);
index e856ca803b8fa263fc3fcfddc60358b87038ddff..20a2f9dc98e5814946d0ba312150bc563f0dd66c 100644 (file)
@@ -236,7 +236,7 @@ public:
                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);
@@ -311,7 +311,7 @@ public:
                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());
        }
@@ -340,7 +340,7 @@ public:
                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())
@@ -355,7 +355,7 @@ public:
                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
index 02f85c1f2c8dd94ddbc179cf33563a74f0bccb9d..a69c75913072db98714386fe310ecf21c3e8dce8 100644 (file)
@@ -112,9 +112,11 @@ void psd_document::read_image_resources()
                                        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;
 
index 4e0f767891430d40f2fdfe46cd07a24898f842a9..1ec096e406f6de8701d1bcfea147fe868484b309 100644 (file)
@@ -28,6 +28,7 @@
 #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>
@@ -89,6 +90,7 @@ core::text::text_info get_text_info(const boost::property_tree::wptree& ptree)
        return result;
 }
 
+
        class layer_link_constructor
        {
                struct linked_layer_record
@@ -201,6 +203,60 @@ boost::rational<int> get_rational(const boost::property_tree::wptree& node)
                        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,
@@ -291,7 +347,7 @@ void create_timelines(
 
 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)
@@ -300,7 +356,7 @@ spl::shared_ptr<core::frame_producer> create_psd_scene_producer(const spl::share
        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;
 
@@ -386,6 +442,9 @@ spl::shared_ptr<core::frame_producer> create_psd_scene_producer(const spl::share
        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());
 
@@ -409,6 +468,22 @@ void init(core::module_dependencies dependencies)
 
                                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);
 }
 
 }}
index c393cbc05064ec2804e9c51720e461b9441d7602..f7d6e9f9c6e9b6bab5fbcb666b58648d62b6de9b 100644 (file)
@@ -235,7 +235,7 @@ bool run()
        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();
 
index 4314f19242bb8cbec63e4f991615f95eac0bedc4..bd4feeea39f58513f23eb8384ac172eed4cb61b8 100644 (file)
@@ -104,15 +104,14 @@ struct server::impl : boost::noncopyable
                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()