]> git.sesse.net Git - casparcg/commitdiff
Advanced expression parsing for creating runtime bindings based on a textual represen...
authorHelge Norberg <helge.norberg@svt.se>
Fri, 23 Aug 2013 20:00:31 +0000 (22:00 +0200)
committerHelge Norberg <helge.norberg@svt.se>
Fri, 23 Aug 2013 20:00:31 +0000 (22:00 +0200)
14 files changed:
core/core.vcxproj
core/core.vcxproj.filters
core/producer/binding.h
core/producer/frame_producer.cpp
core/producer/frame_producer.h
core/producer/scene/expression_parser.cpp [new file with mode: 0644]
core/producer/scene/expression_parser.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/text/text_producer.cpp
core/producer/text/text_producer.h
core/producer/variable.h
modules/oal/consumer/oal_consumer.cpp

index 8b2286b54e81704f134eb09b1fea5fca2b990a7e..e58bf843291eb9d4ce50739f4d2fec513c543390 100644 (file)
     <ClInclude Include="producer\binding.h" />\r
     <ClInclude Include="producer\draw\freehand_producer.h" />\r
     <ClInclude Include="producer\scene\const_producer.h" />\r
+    <ClInclude Include="producer\scene\expression_parser.h" />\r
     <ClInclude Include="producer\scene\hotswap_producer.h" />\r
     <ClInclude Include="producer\scene\scene_producer.h" />\r
     <ClInclude Include="producer\scene\xml_scene_producer.h" />\r
       <PrecompiledHeaderFile Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">../../StdAfx.h</PrecompiledHeaderFile>\r
       <PrecompiledHeaderFile Condition="'$(Configuration)|$(Platform)'=='Release|x64'">../../StdAfx.h</PrecompiledHeaderFile>\r
     </ClCompile>\r
+    <ClCompile Include="producer\scene\expression_parser.cpp">\r
+      <PrecompiledHeaderFile Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">../../StdAfx.h</PrecompiledHeaderFile>\r
+      <PrecompiledHeaderFile Condition="'$(Configuration)|$(Platform)'=='Release|x64'">../../StdAfx.h</PrecompiledHeaderFile>\r
+    </ClCompile>\r
     <ClCompile Include="producer\scene\hotswap_producer.cpp">\r
       <PrecompiledHeaderFile Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">../../StdAfx.h</PrecompiledHeaderFile>\r
       <PrecompiledHeaderFile Condition="'$(Configuration)|$(Platform)'=='Release|x64'">../../StdAfx.h</PrecompiledHeaderFile>\r
index 15ca47fc6082f3c77d6fb96b682195327e9e2b95..83dc27134915a221d06b1eab38e4eca32a6dc35a 100644 (file)
     <ClInclude Include="producer\variable.h">\r
       <Filter>source\producer</Filter>\r
     </ClInclude>\r
+    <ClInclude Include="producer\scene\expression_parser.h">\r
+      <Filter>source\producer\scene</Filter>\r
+    </ClInclude>\r
   </ItemGroup>\r
   <ItemGroup>\r
     <ClCompile Include="producer\transition\transition_producer.cpp">\r
     <ClCompile Include="producer\scene\xml_scene_producer.cpp">\r
       <Filter>source\producer\scene</Filter>\r
     </ClCompile>\r
+    <ClCompile Include="producer\scene\expression_parser.cpp">\r
+      <Filter>source\producer\scene</Filter>\r
+    </ClCompile>\r
   </ItemGroup>\r
 </Project>
\ No newline at end of file
index 2b1df5ba30955b03af884cd1627f49b3c11f0ae3..953e9422f1e1aae04a5723fe666a03f89efe78ed 100644 (file)
 #include <functional>
 #include <memory>
 #include <vector>
+#include <string>
 #include <map>
 #include <algorithm>
+#include <type_traits>
+
+#include <boost/lexical_cast.hpp>
+#include <boost/foreach.hpp>
 
 namespace caspar { namespace core {
 
@@ -46,10 +51,29 @@ struct impl_base : std::enable_shared_from_this<impl_base>
 
        void depend_on(const std::shared_ptr<impl_base>& dependency)
        {
-               dependency->on_change(shared_from_this(), [=] { evaluate(); });
+               auto self = shared_from_this();
+
+               if (dependency->depends_on(self))
+                       throw std::exception("Can't have circular dependencies between bindings");
+
+               dependency->on_change(self, [=] { evaluate(); });
                dependencies_.push_back(dependency);
        }
 
+       bool depends_on(const std::shared_ptr<impl_base>& other) const
+       {
+               BOOST_FOREACH(auto& dependency, dependencies_)
+               {
+                       if (dependency == other)
+                               return true;
+                       
+                       if (dependency->depends_on(other))
+                               return true;            
+               }
+
+               return false;
+       }
+
        void on_change(
                        const std::weak_ptr<void>& dependant,
                        const std::function<void ()>& listener) const
@@ -386,11 +410,32 @@ public:
        }
 
        template<typename T2>
-       binding<T2> as() const
+       typename std::enable_if<
+                       std::is_same<T, T2>::value,
+                       binding<T2>
+               >::type as() const
+       {
+               return *this;
+       }
+
+       template<typename T2>
+       typename std::enable_if<
+                       (std::is_arithmetic<T>::value || std::is_same<bool, T>::value) && (std::is_arithmetic<T2>::value || std::is_same<bool, T2>::value) && !std::is_same<T, T2>::value,
+                       binding<T2>
+               >::type as() const
        {
                return transformed([](T val) { return static_cast<T2>(val); });
        }
 
+       template<typename T2>
+       typename std::enable_if<
+                       (std::is_same<std::wstring, T>::value || std::is_same<std::wstring, T2>::value) && !std::is_same<T, T2>::value,
+                       binding<T2>
+               >::type as() const
+       {
+               return transformed([](T val) { return boost::lexical_cast<T2>(val); });
+       }
+
        // Func -> R (T self_val)
        // Returns binding<R>
        template<typename Func>
@@ -452,11 +497,21 @@ static binding<bool> operator||(const binding<bool>& lhs, const binding<bool>& r
        return lhs.composed(rhs, [](bool lhs, bool rhs) { return lhs || rhs; });
 }
 
+static binding<bool> operator||(const binding<bool>& lhs, bool rhs)
+{
+       return lhs.transformed([rhs](bool lhs) { return lhs || rhs; });
+}
+
 static binding<bool> operator&&(const binding<bool>& lhs, const binding<bool>& rhs)
 {
        return lhs.composed(rhs, [](bool lhs, bool rhs) { return lhs && rhs; });
 }
 
+static binding<bool> operator&&(const binding<bool>& lhs, bool rhs)
+{
+       return lhs.transformed([rhs](bool lhs) { return lhs && rhs; });
+}
+
 static binding<bool> operator!(const binding<bool>& self)
 {
        return self.transformed([](bool self) { return !self; });
index 4469a53f33e49b6a27e537955dc8f23624d1f53f..e3c7e16964a741d57177d1906a343ca93d0a3320 100644 (file)
@@ -29,6 +29,7 @@
 #include "color/color_producer.h"
 #include "draw/freehand_producer.h"
 #include "separated/separated_producer.h"
+#include "variable.h"
 
 #include <common/assert.h>
 #include <common/except.h>
@@ -130,6 +131,19 @@ uint32_t frame_producer_base::frame_number() const
        return impl_->frame_number_;
 }
 
+variable& frame_producer_base::get_variable(const std::wstring& name)
+{
+       CASPAR_THROW_EXCEPTION(caspar_exception() 
+                       << msg_info(L"No variable called " + name + L" found in " + print()));
+}
+
+const std::vector<std::wstring>& frame_producer_base::get_variables() const
+{
+       static std::vector<std::wstring> empty;
+
+       return empty;
+}
+
 const spl::shared_ptr<frame_producer>& frame_producer::empty() 
 {
        class empty_frame_producer : public frame_producer
@@ -145,6 +159,8 @@ const spl::shared_ptr<frame_producer>& frame_producer::empty()
                std::wstring name() const override {return L"empty";}
                uint32_t frame_number() const override {return 0;}
                boost::unique_future<std::wstring> call(const std::vector<std::wstring>& params) override{CASPAR_THROW_EXCEPTION(not_supported());}
+               variable& get_variable(const std::wstring& name) override { CASPAR_THROW_EXCEPTION(not_supported()); }
+               const std::vector<std::wstring>& get_variables() const override { static std::vector<std::wstring> empty; return empty; }
                draw_frame last_frame() {return draw_frame::empty();}
                constraints& pixel_constraints() override { static constraints c; return c; }
        
@@ -214,6 +230,8 @@ public:
        uint32_t                                                                                        frame_number() const override                                                                                                   {return producer_->frame_number();}
        boost::property_tree::wptree                                            info() const override                                                                                                                   {return producer_->info();}
        boost::unique_future<std::wstring>                                      call(const std::vector<std::wstring>& params) override                                                  {return producer_->call(params);}
+       variable&                                                                                       get_variable(const std::wstring& name) override                                                                 {return producer_->get_variable(name);}
+       const std::vector<std::wstring>&                                        get_variables() const override                                                                                                  {return producer_->get_variables();}
        void                                                                                            leading_producer(const spl::shared_ptr<frame_producer>& producer) override              {return producer_->leading_producer(producer);}
        uint32_t                                                                                        nb_frames() const override                                                                                                              {return producer_->nb_frames();}
        class draw_frame                                                                        last_frame()                                                                                                                                    {return producer_->last_frame();}
index 85b7d77c8e8617b20f62df97827185f16ad03f42..14a391466e20dccb381d8d5826ff862a8ce3158f 100644 (file)
@@ -44,6 +44,8 @@ FORWARD1(caspar, class executor);
 
 namespace caspar { namespace core {
 
+class variable;
+
 struct constraints
 {
        binding<double> width;
@@ -73,6 +75,8 @@ public:
 
        virtual class draw_frame                                        receive() = 0;
        virtual boost::unique_future<std::wstring>      call(const std::vector<std::wstring>& params) = 0;
+       virtual variable&                                                       get_variable(const std::wstring& name) = 0;
+       virtual const std::vector<std::wstring>&        get_variables() const = 0;
        
        // monitor::observable
 
@@ -105,7 +109,9 @@ public:
 
        // Methods      
 
-       virtual boost::unique_future<std::wstring>      call(const std::vector<std::wstring>& params);
+       virtual boost::unique_future<std::wstring>      call(const std::vector<std::wstring>& params) override;
+       virtual variable&                                                       get_variable(const std::wstring& name) override;
+       virtual const std::vector<std::wstring>&        get_variables() const override;
        
        // monitor::observable
        
diff --git a/core/producer/scene/expression_parser.cpp b/core/producer/scene/expression_parser.cpp
new file mode 100644 (file)
index 0000000..2735c42
--- /dev/null
@@ -0,0 +1,751 @@
+/*
+* 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 "expression_parser.h"
+
+#include <string>
+#include <memory>
+#include <vector>
+#include <functional>
+#include <typeinfo>
+
+#include <boost/any.hpp>
+
+#include <common/log.h>
+#include <common/except.h>
+#include <common/utf.h>
+
+namespace caspar { namespace core { namespace scene {
+
+wchar_t next_non_whitespace(
+               std::wstring::const_iterator& cursor,
+               const std::wstring& str,
+               const std::wstring& error_if_eof)
+{
+       while (cursor != str.end())
+       {
+               switch (*cursor)
+               {
+               case L' ':
+               case L'\t':
+                       ++cursor;
+                       continue;
+               default:
+                       return *cursor;
+               }
+       }
+
+       CASPAR_THROW_EXCEPTION(caspar_exception() << msg_info(
+                       L"Unexpected end of input (" + error_if_eof + L") in " + str));
+}
+
+std::wstring at_position(
+               const std::wstring::const_iterator& cursor, const std::wstring& str)
+{
+       int index = static_cast<int>(cursor - str.begin());
+
+       return L" at index " + boost::lexical_cast<std::wstring>(index)
+                       + L" in " + str;
+}
+
+boost::any parse_expression(
+               std::wstring::const_iterator& cursor,
+               const std::wstring& str,
+               const variable_repository& var_repo);
+
+boost::any parse_parenthesis(
+               std::wstring::const_iterator& cursor,
+               const std::wstring& str,
+               const variable_repository& var_repo)
+{
+       if (*cursor++ != L'(')
+               CASPAR_THROW_EXCEPTION(caspar_exception()
+                               << msg_info(L"Expected (" + at_position(cursor, str)));
+
+       auto expr = parse_expression(cursor, str, var_repo);
+
+       if (*cursor++ != L')')
+               CASPAR_THROW_EXCEPTION(caspar_exception()
+                               << msg_info(L"Expected )" + at_position(cursor, str)));
+
+       return expr;
+}
+
+double parse_constant(
+               std::wstring::const_iterator& cursor, const std::wstring& str)
+{
+       std::wstring constant;
+
+       while (cursor != str.end())
+       {
+               wchar_t ch = *cursor;
+
+               if ((ch >= L'0' && ch <= L'9') || ch == L'.')
+                       constant += ch;
+               else
+                       break;
+
+               ++cursor;
+       }
+
+       return boost::lexical_cast<double>(constant);
+}
+
+std::wstring parse_string_literal(
+               std::wstring::const_iterator& cursor, const std::wstring& str)
+{
+       std::wstring literal;
+
+       if (*cursor++ != L'"')
+               CASPAR_THROW_EXCEPTION(caspar_exception()
+                               << msg_info(L"Expected (" + at_position(cursor, str)));
+
+       bool escaping = false;
+
+       while (cursor != str.end())
+       {
+               wchar_t ch = *cursor;
+
+               switch (ch)
+               {
+               case L'\\':
+                       if (escaping)
+                       {
+                               literal += ch;
+                               escaping = false;
+                       }
+                       else
+                               escaping = true;
+                       break;
+               case L'"':
+                       if (escaping)
+                       {
+                               literal += ch;
+                               escaping = false;
+                               break;
+                       }
+                       else
+                       {
+                               ++cursor;
+                               return std::move(literal);
+                       }
+               case L'n':
+                       if (escaping)
+                       {
+                               literal += L'\n';
+                               escaping = false;
+                       }
+                       else
+                               literal += ch;
+                       break;
+               default:
+                       literal += ch;
+               }
+
+               ++cursor;
+       }
+
+       CASPAR_THROW_EXCEPTION(caspar_exception() << msg_info(
+                       L"Unexpected end of input (Expected closing \") in " + str));
+}
+
+boost::any parse_variable(
+               std::wstring::const_iterator& cursor,
+               const std::wstring& str,
+               const variable_repository& var_repo)
+{
+       std::wstring variable_name;
+
+       while (cursor != str.end())
+       {
+               wchar_t ch = *cursor;
+
+               if (ch == L'.'
+                               || ch == L'_'
+                               || (ch >= L'a' && ch <= L'z')
+                               || (ch >= L'A' && ch <= L'Z'))
+                       variable_name += ch;
+               else
+                       break;
+
+               ++cursor;
+       }
+
+       if (variable_name == L"true")
+               return true;
+       else if (variable_name == L"false")
+               return false;
+
+       variable& var = var_repo(variable_name);
+
+       if (var.is<double>())
+               return var.as<double>();
+       else if (var.is<int64_t>())
+               return var.as<int64_t>().as<double>();
+       else if (var.is<std::wstring>())
+               return var.as<std::wstring>();
+       else if (var.is<bool>())
+               return var.as<bool>();
+
+       CASPAR_THROW_EXCEPTION(caspar_exception() << msg_info(
+                               L"Unhandled variable type of " + variable_name
+                               + at_position(cursor, str)));
+}
+
+struct op
+{
+       enum op_type
+       {
+               UNARY,
+               BINARY,
+               TERNARY
+       };
+
+       std::wstring characters;
+       int precedence;
+       op_type type;
+
+       op(wchar_t ch, int precedence, op_type type)
+               : characters(1, ch), precedence(precedence), type(type)
+       {
+       }
+
+       op(const std::wstring& chs, int precedence, op_type type)
+               : characters(chs), precedence(precedence), type(type)
+       {
+       }
+};
+
+op parse_operator(std::wstring::const_iterator& cursor, const std::wstring& str)
+{
+       std::wstring characters;
+       static const wchar_t NONE = L' ';
+       wchar_t first = NONE;
+
+       while (cursor != str.end())
+       {
+               wchar_t ch = *cursor;
+
+               switch (ch)
+               {
+               case L'+':
+                       ++cursor;
+                       return op(ch, 6, op::BINARY);
+               case L'*':
+               case L'/':
+               case L'%':
+                       ++cursor;
+                       return op(ch, 5, op::BINARY);
+               case L'?':
+               case L':':
+                       ++cursor;
+                       return op(ch, 15, op::TERNARY);
+               case L'-':
+                       if (first == L'-')
+                               CASPAR_THROW_EXCEPTION(caspar_exception() << msg_info(
+                                               L"Did not expect -" + at_position(cursor, str)));
+                       else
+                               first = ch;
+                       
+                       ++cursor;
+                       break;
+               case L'!':
+                       if (first == L'!')
+                               CASPAR_THROW_EXCEPTION(caspar_exception() << msg_info(
+                                               L"Did not expect !" + at_position(cursor, str)));
+                       else
+                               first = ch;
+
+                       ++cursor;
+                       break;
+               case L'<':
+                       if (first == L'<')
+                               CASPAR_THROW_EXCEPTION(caspar_exception() << msg_info(
+                                               L"Did not expect <" + at_position(cursor, str)));
+                       else
+                               first = ch;
+
+                       ++cursor;
+                       break;
+               case L'>':
+                       if (first == L'>')
+                               CASPAR_THROW_EXCEPTION(caspar_exception() << msg_info(
+                                               L"Did not expect >" + at_position(cursor, str)));
+                       else
+                               first = ch;
+
+                       ++cursor;
+                       break;
+               case L'=':
+                       if (first == L'=')
+                       {
+                               ++cursor;
+                               return op(L"==", 9, op::BINARY);
+                       }
+                       else if (first == L'!')
+                       {
+                               ++cursor;
+                               return op(L"!=", 9, op::BINARY);
+                       }
+                       else if (first == L'>')
+                       {
+                               ++cursor;
+                               return op(L">=", 8, op::BINARY);
+                       }
+                       else if (first == L'<')
+                       {
+                               ++cursor;
+                               return op(L"<=", 8, op::BINARY);
+                       }
+                       else if (first == NONE)
+                       {
+                               ++cursor;
+                               first = L'=';
+                       }
+                       else
+                               CASPAR_THROW_EXCEPTION(caspar_exception() << msg_info(
+                                               L"Did not expect =" + at_position(cursor, str)));
+
+                       break;
+               case L'|':
+                       if (first == L'|')
+                       {
+                               ++cursor;
+                               return op(L"||", 14, op::BINARY);
+                       }
+                       else if (first == NONE)
+                       {
+                               ++cursor;
+                               first = L'|';
+                       }
+                       else
+                               CASPAR_THROW_EXCEPTION(caspar_exception() << msg_info(
+                                               L"Did not expect =" + at_position(cursor, str)));
+
+                       break;
+               case L'&':
+                       if (first == L'&')
+                       {
+                               ++cursor;
+                               return op(L"&&", 13, op::BINARY);
+                       }
+                       else if (first == NONE)
+                       {
+                               ++cursor;
+                               first = L'&';
+                       }
+                       else
+                               CASPAR_THROW_EXCEPTION(caspar_exception() << msg_info(
+                                               L"Did not expect =" + at_position(cursor, str)));
+
+                       break;
+               case L' ':
+               case L'\t':
+                       if (first == L'-')
+                               return op(L'-', 6, op::BINARY);
+                       else if (first == L'!')
+                               return op(L'!', 3, op::UNARY);
+               default:
+                       if (first == L'<')
+                               return op(L'<', 8, op::BINARY);
+                       else if (first == L'>')
+                               return op(L'>', 8, op::BINARY);
+                       else if (first == L'-')
+                               return op(L"unary-", 3, op::UNARY);
+                       else if (first == L'!')
+                               return op(L'!', 3, op::UNARY);
+                       else
+                               CASPAR_THROW_EXCEPTION(caspar_exception() << msg_info(
+                                               L"Expected second character of operator"
+                                               + at_position(cursor, str)));
+               }
+       }
+
+       CASPAR_THROW_EXCEPTION(caspar_exception() << msg_info(
+                       L"Unexpected end of input (Expected operator) in " + str));
+}
+
+template<typename T>
+T as(const boost::any& value)
+{
+       return boost::any_cast<T>(value);
+}
+
+boost::any as_binding(const boost::any& value)
+{
+       // Wrap supported constants as bindings
+       if (is<double>(value))
+               return binding<double>(as<double>(value));
+       else if (is<bool>(value))
+               return binding<bool>(as<bool>(value));
+       else if (is<std::wstring>(value))
+               return binding<std::wstring>(as<std::wstring>(value));
+       // Already one of the supported binding types
+       else if (is<binding<double>>(value))
+               return as<binding<double>>(value);
+       else if (is<binding<bool>>(value))
+               return as<binding<bool>>(value);
+       else if (is<binding<std::wstring>>(value))
+               return as<binding<std::wstring>>(value);
+       else
+               CASPAR_THROW_EXCEPTION(caspar_exception() << msg_info(
+                               L"Couldn't detect type of " + u16(value.type().name())));
+}
+
+template<typename T>
+binding<T> require(const boost::any& value)
+{
+       auto b = as_binding(value);
+
+       if (is<binding<T>>(b))
+               return as<binding<T>>(b);
+       else
+               CASPAR_THROW_EXCEPTION(caspar_exception() << msg_info(
+                               L"Required binding of type " + u16(typeid(T).name())
+                               + L" but got " + u16(value.type().name())));
+}
+
+boost::any negative(const boost::any& to_create_negative_of)
+{
+       return -require<double>(to_create_negative_of);
+}
+
+boost::any not(const boost::any& to_create_not_of)
+{
+       return !require<bool>(to_create_not_of);
+}
+
+boost::any multiply(const boost::any& lhs, boost::any& rhs)
+{
+       return require<double>(lhs) * require<double>(rhs);
+}
+
+boost::any divide(const boost::any& lhs, boost::any& rhs)
+{
+       return require<double>(lhs) / require<double>(rhs);
+}
+
+boost::any modulus(const boost::any& lhs, boost::any& rhs)
+{
+       return
+                       (
+                                       require<double>(lhs).as<int64_t>()
+                                       % require<double>(rhs).as<int64_t>()
+                       ).as<double>();
+}
+
+binding<std::wstring> stringify(const boost::any& value)
+{
+       auto b = as_binding(value);
+
+       if (is<binding<std::wstring>>(b))
+               return as<binding<std::wstring>>(b);
+       else if (is<binding<double>>(b))
+               return as<binding<double>>(b).as<std::wstring>();
+       else if (is<binding<bool>>(b))
+               return as<binding<bool>>(b).as<std::wstring>();
+       else
+               CASPAR_THROW_EXCEPTION(caspar_exception()
+                               << msg_info(L"Couldn't stringify " + u16(value.type().name())));
+}
+
+boost::any add(const boost::any& lhs, boost::any& rhs)
+{
+       auto l = as_binding(lhs);
+       auto r = as_binding(rhs);
+
+       // number
+       if (is<binding<double>>(l) && is<binding<double>>(r))
+               return as<binding<double>>(l) + as<binding<double>>(r);
+       // string
+       else if (is<binding<std::wstring>>(l) && is<binding<std::wstring>>(r))
+               return as<binding<std::wstring>>(l) + as<binding<std::wstring>>(r);
+       // mixed types to string and concatenated
+       else
+               return stringify(lhs) + stringify(rhs);
+}
+
+boost::any subtract(const boost::any& lhs, boost::any& rhs)
+{
+       return require<double>(lhs) - require<double>(rhs);
+}
+
+boost::any less(const boost::any& lhs, boost::any& rhs)
+{
+       return require<double>(lhs) < require<double>(rhs);
+}
+
+boost::any less_or_equal(const boost::any& lhs, boost::any& rhs)
+{
+       return require<double>(lhs) <= require<double>(rhs);
+}
+
+boost::any greater(const boost::any& lhs, boost::any& rhs)
+{
+       return require<double>(lhs) > require<double>(rhs);
+}
+
+boost::any greater_or_equal(const boost::any& lhs, boost::any& rhs)
+{
+       return require<double>(lhs) >= require<double>(rhs);
+}
+
+boost::any equal(const boost::any& lhs, boost::any& rhs)
+{
+       auto l = as_binding(lhs);
+       auto r = as_binding(rhs);
+
+       // number
+       if (is<binding<double>>(l) && is<binding<double>>(r))
+               return as<binding<double>>(l) == as<binding<double>>(r);
+       // string
+       else if (is<binding<std::wstring>>(l) && is<binding<std::wstring>>(r))
+               return as<binding<std::wstring>>(l) == as<binding<std::wstring>>(r);
+       // boolean
+       else
+               return require<bool>(l) == require<bool>(r);
+}
+
+boost::any and(const boost::any& lhs, boost::any& rhs)
+{
+       return require<bool>(lhs) && require<bool>(rhs);
+}
+
+boost::any or(const boost::any& lhs, boost::any& rhs)
+{
+       return require<bool>(lhs) || require<bool>(rhs);
+}
+
+template<typename T>
+binding<T> ternary(
+               const binding<bool>& condition,
+               const binding<T>& true_value,
+               const binding<T>& false_value)
+{
+       return when(condition).then(true_value).otherwise(false_value);
+}
+
+boost::any ternary(
+               const boost::any& condition,
+               const boost::any& true_value,
+               const boost::any& false_value)
+{
+       auto cond = require<bool>(condition);
+       auto t = as_binding(true_value);
+       auto f = as_binding(false_value);
+       
+       // double
+       if (is<binding<double>>(t) && is<binding<double>>(f))
+               return ternary(cond, as<binding<double>>(t), as<binding<double>>(f));
+       // string
+       else if (is<binding<std::wstring>>(t) && is<binding<std::wstring>>(f))
+               return ternary(
+                               cond,
+                               as<binding<std::wstring>>(t),
+                               as<binding<std::wstring>>(f));
+       // bool
+       else
+               return ternary(cond, require<bool>(t), require<bool>(f));
+}
+
+void resolve_operators(int precedence, std::vector<boost::any>& tokens)
+{
+       for (int i = 0; i < tokens.size(); ++i)
+       {
+               auto& token = tokens.at(i);
+
+               if (!is<op>(token))
+                       continue;
+
+               auto op_token = as<op>(token);
+
+               if (op_token.precedence != precedence)
+                       continue;
+
+               int index_after = i + 1;
+               auto& token_after = tokens.at(index_after);
+
+               switch (op_token.type)
+               {
+               case op::UNARY:
+                       if (op_token.characters == L"unary-")
+                       {
+                               tokens.at(i) = negative(token_after);
+                       }
+                       else if (op_token.characters == L"!")
+                       {
+                               tokens.at(i) = not(token_after);
+                       }
+
+                       tokens.erase(tokens.begin() + index_after);
+
+                       break;
+               case op::BINARY:
+                       {
+                               auto& token_before = tokens.at(i - 1);
+
+                               if (op_token.characters == L"*")
+                                       token_before = multiply(token_before, token_after);
+                               else if (op_token.characters == L"/")
+                                       token_before = divide(token_before, token_after);
+                               else if (op_token.characters == L"%")
+                                       token_before = modulus(token_before, token_after);
+                               else if (op_token.characters == L"+")
+                                       token_before = add(token_before, token_after);
+                               else if (op_token.characters == L"-")
+                                       token_before = subtract(token_before, token_after);
+                               else if (op_token.characters == L"<")
+                                       token_before = less(token_before, token_after);
+                               else if (op_token.characters == L"<=")
+                                       token_before = less_or_equal(token_before, token_after);
+                               else if (op_token.characters == L">")
+                                       token_before = greater(token_before, token_after);
+                               else if (op_token.characters == L">=")
+                                       token_before = greater_or_equal(token_before, token_after);
+                               else if (op_token.characters == L"==")
+                                       token_before = equal(token_before, token_after);
+                               else if (op_token.characters == L"!=")
+                                       token_before = not(equal(token_before, token_after));
+                               else if (op_token.characters == L"&&")
+                                       token_before = and(token_before, token_after);
+                               else if (op_token.characters == L"||")
+                                       token_before = or(token_before, token_after);
+                       }
+
+                       tokens.erase(tokens.begin() + i, tokens.begin() + i + 2);
+                       --i;
+
+                       break;
+               case op::TERNARY:
+                       if (op_token.characters == L"?")
+                       {
+                               auto& token_before = tokens.at(i - 1);
+                               auto& token_colon_operator = tokens.at(i + 2);
+
+                               if (as<op>(token_colon_operator).characters != L":")
+                                       CASPAR_THROW_EXCEPTION(caspar_exception() << msg_info(
+                                                       L"Expected : as part of ternary expression"));
+
+                               auto& token_false_value = tokens.at(i + 3);
+                               token_before = ternary(
+                                               token_before, token_after, token_false_value);
+                               tokens.erase(tokens.begin() + i, tokens.begin() + i + 4);
+                               --i;
+                       }
+
+                       break;
+               }
+       }
+}
+
+boost::any parse_expression(
+               std::wstring::const_iterator& cursor,
+               const std::wstring& str,
+               const variable_repository& var_repo)
+{
+       std::vector<boost::any> tokens;
+       bool stop = false;
+
+       while (cursor != str.end())
+       {
+               wchar_t ch = next_non_whitespace(cursor, str, L"Expected expression");
+
+               switch (ch)
+               {
+               case L'0':
+               case L'1':
+               case L'2':
+               case L'3':
+               case L'4':
+               case L'5':
+               case L'6':
+               case L'7':
+               case L'8':
+               case L'9':
+                       tokens.push_back(parse_constant(cursor, str));
+                       break;
+               case L'+':
+               case L'-':
+               case L'*':
+               case L'/':
+               case L'%':
+               case L'<':
+               case L'>':
+               case L'!':
+               case L'=':
+               case L'|':
+               case L'&':
+               case L'?':
+               case L':':
+                       tokens.push_back(parse_operator(cursor, str));
+                       break;
+               case L'"':
+                       tokens.push_back(parse_string_literal(cursor, str));
+                       break;
+               case L'(':
+                       tokens.push_back(parse_parenthesis(cursor, str, var_repo));
+                       break;
+               case L')':
+                       stop = true;
+                       break;
+               default:
+                       tokens.push_back(parse_variable(cursor, str, var_repo));
+                       break;
+               }
+
+               if (stop)
+                       break;
+       }
+
+       if (tokens.empty())
+               CASPAR_THROW_EXCEPTION(caspar_exception()
+                               << msg_info(L"Expected expression"));
+
+       int precedence = 1;
+
+       while (tokens.size() > 1)
+       {
+               resolve_operators(precedence++, tokens);
+       }
+
+       return as_binding(tokens.at(0));
+}
+
+template<>
+binding<std::wstring> parse_expression(
+               const std::wstring& str, const variable_repository& var_repo)
+{
+       auto cursor = str.cbegin();
+       auto expr = parse_expression(cursor, str, var_repo);
+
+       if (is<binding<std::wstring>>(expr))
+               return as<binding<std::wstring>>(expr);
+       else if (is<binding<double>>(expr))
+               return as<binding<double>>(expr).as<std::wstring>();
+       else
+               CASPAR_THROW_EXCEPTION(caspar_exception() << msg_info(
+                               L"parse_expression() Unsupported type "
+                               + u16(expr.type().name())));
+}
+
+}}}
diff --git a/core/producer/scene/expression_parser.h b/core/producer/scene/expression_parser.h
new file mode 100644 (file)
index 0000000..0ef6e1a
--- /dev/null
@@ -0,0 +1,60 @@
+/*
+* 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 "../variable.h"
+
+namespace caspar { namespace core { namespace scene {
+
+typedef std::function<variable& (const std::wstring& name)> variable_repository;
+
+boost::any parse_expression(
+               std::wstring::const_iterator& cursor,
+               const std::wstring& str,
+               const variable_repository& var_repo);
+
+template<typename T>
+bool is(const boost::any& value)
+{
+       return value.type() == typeid(T);
+}
+
+template<typename T>
+static binding<T> parse_expression(
+               const std::wstring& str, const variable_repository& var_repo)
+{
+       auto cursor = str.cbegin();
+       auto expr = parse_expression(cursor, str, var_repo);
+
+       if (is<binding<T>>(expr))
+               return boost::any_cast<binding<T>>(expr);
+       else
+               CASPAR_THROW_EXCEPTION(caspar_exception() << msg_info(
+                               L"parse_expression() Unsupported type "
+                               + u16(expr.type().name())));
+}
+
+template<>
+binding<std::wstring> parse_expression(
+               const std::wstring& str, const variable_repository& var_repo);
+
+}}}
index 6b1b6b8f0b5a579e7cb61559225a74085a0afc1d..133871aab425d9e16467166a2359474d76576fc1 100644 (file)
@@ -94,15 +94,19 @@ struct scene_producer::impl
        double frame_fraction_;
        std::map<void*, timeline> timelines_;
        std::map<std::wstring, std::shared_ptr<core::variable>> variables_;
+       std::vector<std::wstring> variable_names_;
 
        impl(int width, int height)
                : pixel_constraints_(width, height)
                , aggregator_([=] (double x, double y) { return collission_detect(x, y); })
                , frame_fraction_(0)
        {
-               auto speed_variable = std::make_shared<core::variable_impl<double>>(L"", true, 1.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);
+               store_variable(L"frame", frame_variable);
+               frame_number_ = frame_variable->value();
        }
 
        layer& create_layer(
@@ -127,6 +131,7 @@ struct scene_producer::impl
                        const std::wstring& name, const std::shared_ptr<core::variable>& var)
        {
                variables_.insert(std::make_pair(name, var));
+               variable_names_.push_back(name);
        }
 
        core::variable& get_variable(const std::wstring& name)
@@ -139,6 +144,11 @@ struct scene_producer::impl
                return *found->second;
        }
 
+       const std::vector<std::wstring>& get_variables() const
+       {
+               return variable_names_;
+       }
+
        binding<int64_t> frame()
        {
                return frame_number_;
@@ -150,6 +160,8 @@ struct scene_producer::impl
 
                auto& pos = transform.image_transform.fill_translation;
                auto& scale = transform.image_transform.fill_scale;
+               //auto& clip_pos = transform.image_transform.clip_translation;
+               //auto& clip_scale = transform.image_transform.clip_scale;
 
                pos[0] = static_cast<double>(layer.position.x.get()) / static_cast<double>(pixel_constraints_.width.get());
                pos[1] = static_cast<double>(layer.position.y.get()) / static_cast<double>(pixel_constraints_.height.get());
@@ -158,6 +170,11 @@ struct scene_producer::impl
                scale[1] = static_cast<double>(layer.producer.get()->pixel_constraints().height.get())
                                / static_cast<double>(pixel_constraints_.height.get());
 
+               /*clip_pos[0] = static_cast<double>(layer.clipping.upper_left.x.get()) / static_cast<double>(pixel_constraints_.width.get());
+               clip_pos[1] = static_cast<double>(layer.clipping.upper_left.y.get()) / static_cast<double>(pixel_constraints_.height.get());
+               clip_scale[0] = static_cast<double>(layer.clipping.width.get()) / static_cast<double>(pixel_constraints_.width.get());
+               clip_scale[1] = static_cast<double>(layer.clipping.height.get()) / static_cast<double>(pixel_constraints_.height.get());*/
+
                transform.image_transform.opacity = layer.adjustments.opacity.get();
                transform.image_transform.is_key = layer.is_key.get();
 
@@ -354,6 +371,11 @@ core::variable& scene_producer::get_variable(const std::wstring& name)
        return impl_->get_variable(name);
 }
 
+const std::vector<std::wstring>& scene_producer::get_variables() const
+{
+       return impl_->get_variables();
+}
+
 spl::shared_ptr<core::frame_producer> create_dummy_scene_producer(const spl::shared_ptr<core::frame_factory>& frame_factory, const video_format_desc& format_desc, const std::vector<std::wstring>& params)
 {
        if (params.size() < 1 || !boost::iequals(params.at(0), L"[SCENE]"))
@@ -389,7 +411,14 @@ spl::shared_ptr<core::frame_producer> create_dummy_scene_producer(const spl::sha
                return result;
        };
 
-       //auto& car_layer = scene->create_layer(create_producer(frame_factory, format_desc, create_param(L"car")));
+       auto& car_layer = scene->create_layer(create_producer(frame_factory, format_desc, create_param(L"car")), L"car");
+       car_layer.clipping.upper_left.x.set(80);
+       car_layer.clipping.upper_left.y.set(45);
+       car_layer.clipping.width.unbind();
+       car_layer.clipping.width.set(640);
+       car_layer.clipping.height.unbind();
+       car_layer.clipping.height.set(360);
+       car_layer.adjustments.opacity.set(0.5);
        //car_layer.hidden = scene->frame() % 50 > 25 || !(scene->frame() < 1000);
        std::vector<std::wstring> sub_params;
        sub_params.push_back(L"[FREEHAND]");
index 718dabfcf99db1dbc5281f37236f7ebda3aabc9b..5e8f27b057d7a86905a91ff2d11e206c5e9f2265 100644 (file)
@@ -166,7 +166,7 @@ public:
 
                                        to_affect.set(tweened);
                                        
-                                       CASPAR_LOG(info) << relative_frame << L" " << *start_value << L" " << duration << L" " << tweened;
+                                       //CASPAR_LOG(info) << relative_frame << L" " << *start_value << L" " << duration << L" " << tweened;
                                };
 
                store_keyframe(to_affect.identity(), k);
@@ -191,7 +191,8 @@ public:
                store_keyframe(to_affect.identity(), k);
        }
 
-       core::variable& get_variable(const std::wstring& name);
+       core::variable& get_variable(const std::wstring& name) override;
+       const std::vector<std::wstring>& get_variables() const override;
 private:
        void store_keyframe(void* timeline_identity, const keyframe& k);
        void store_variable(
index 666926b6e084e8255e6e726a006146d9d83fbf89..ab71e6371209269a40238ea293ee8bad906f397c 100644 (file)
@@ -22,6 +22,7 @@
 #include "../../stdafx.h"
 
 #include "xml_scene_producer.h"
+#include "expression_parser.h"
 
 #include <boost/filesystem.hpp>
 #include <boost/property_tree/ptree.hpp>
 
 namespace caspar { namespace core { namespace scene {
 
+void deduce_expression(variable& var, const variable_repository& repo)
+{
+       auto expr_str = var.original_expr();
+       auto trimmed = boost::trim_copy(expr_str);
+
+       if (boost::starts_with(trimmed, L"${") && boost::ends_with(trimmed, L"}"))
+       {
+               expr_str = trimmed.substr(2, expr_str.length() - 3);
+
+               if (var.is<double>())
+                       var.as<double>().bind(parse_expression<double>(expr_str, repo));
+               else if (var.is<bool>())
+                       var.as<bool>().bind(parse_expression<bool>(expr_str, repo));
+               else if (var.is<std::wstring>())
+                       var.as<std::wstring>().bind(parse_expression<std::wstring>(expr_str, repo));
+       }
+       else if (!expr_str.empty())
+       {
+               var.from_string(expr_str);
+       }
+}
+
 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,
@@ -63,12 +86,19 @@ spl::shared_ptr<core::frame_producer> create_xml_scene_producer(
        BOOST_FOREACH(auto elem, root.get_child(L"scene.variables"))
        {
                auto type = elem.second.get<std::wstring>(L"<xmlattr>.type");
-
-               if (type == L"double")
-                       scene->create_variable<double>(
-                                       L"variable." + elem.second.get<std::wstring>(L"<xmlattr>.id"),
-                                       false,
-                                       elem.second.get_value<std::wstring>());
+               auto id = elem.second.get<std::wstring>(L"<xmlattr>.id");
+               auto is_public = elem.second.get(L"<xmlattr>.public", false);
+               auto expr = elem.second.get_value<std::wstring>();
+
+               if (!is_public)
+                       id = L"variable." + id;
+
+               if (type == L"number")
+                       scene->create_variable<double>(id, is_public, expr);
+               else if (type == L"string")
+                       scene->create_variable<std::wstring>(id, is_public, expr);
+               else if (type == L"bool")
+                       scene->create_variable<bool>(id, is_public, expr);
        }
 
        BOOST_FOREACH(auto elem, root.get_child(L"scene.layers"))
@@ -82,8 +112,25 @@ spl::shared_ptr<core::frame_producer> create_xml_scene_producer(
                layer.position.x = scene->create_variable<double>(variable_prefix + L"x", false, elem.second.get<std::wstring>(L"x"));
                layer.position.y = scene->create_variable<double>(variable_prefix + L"y", false, elem.second.get<std::wstring>(L"y"));
 
+               layer.adjustments.opacity = scene->create_variable<double>(variable_prefix + L"adjustment.opacity", false, elem.second.get(L"adjustments.opacity", L"1.0"));
+
                scene->create_variable<double>(variable_prefix + L"width", false) = layer.producer.get()->pixel_constraints().width;
                scene->create_variable<double>(variable_prefix + L"height", false) = layer.producer.get()->pixel_constraints().height;
+
+               BOOST_FOREACH(auto& var_name, producer->get_variables())
+               {
+                       auto& var = producer->get_variable(var_name);
+                       auto expr = elem.second.get<std::wstring>(L"parameters." + var_name, L"");
+
+                       if (var.is<double>())
+                               scene->create_variable<double>(variable_prefix + L"parameter." + var_name, false, expr) = var.as<double>();
+                       else if (var.is<std::wstring>())
+                               scene->create_variable<std::wstring>(variable_prefix + L"parameter." + var_name, false, expr) = var.as<std::wstring>();
+                       else if (var.is<bool>())
+                               scene->create_variable<bool>(variable_prefix + L"parameter." + var_name, false, expr) = var.as<bool>();
+                       else if (var.is<int>())
+                               scene->create_variable<double>(variable_prefix + L"parameter." + var_name, false, expr) = var.as<int>().as<double>();
+               }
        }
 
        BOOST_FOREACH(auto& elem, root.get_child(L"scene.timelines"))
@@ -105,17 +152,21 @@ spl::shared_ptr<core::frame_producer> create_xml_scene_producer(
                }
        }
 
-       BOOST_FOREACH(auto& elem, root.get_child(L"scene.parameters"))
+       auto repo = [&scene](const std::wstring& name) -> variable&
        {
-               auto& variable = scene->get_variable(elem.second.get<std::wstring>(L"<xmlattr>.variable"));
-               auto id = elem.second.get<std::wstring>(L"<xmlattr>.id");
+               return scene->get_variable(name); 
+       };
 
-               if (variable.is<double>())
-                       scene->create_variable<double>(id, true) = variable.as<double>();
-               else if (variable.is<std::wstring>())
-                       scene->create_variable<std::wstring>(id, true) = variable.as<std::wstring>();
+       BOOST_FOREACH(auto& var_name, scene->get_variables())
+       {
+               deduce_expression(scene->get_variable(var_name), repo);
        }
 
+       auto params2 = params;
+       params2.erase(params2.begin());
+
+       scene->call(params2);
+
        return scene;
 }
 
index c021de59703d8e40920ebda189c84c8398863520..18669fc62c269ff4469eda661c9a9cb79ee44cfa 100644 (file)
@@ -25,6 +25,7 @@
 
 #include <core/producer/frame_producer.h>
 #include <core/producer/color/color_producer.h>
+#include <core/producer/variable.h>
 #include <core/frame/geometry.h>
 #include <core/frame/frame.h>
 #include <core/frame/draw_frame.h>
@@ -131,10 +132,10 @@ struct text_producer::impl
        constraints constraints_;
        int x_, y_, parent_width_, parent_height_;
        bool standalone_;
-       binding<std::wstring> text_;
+       variable_impl<std::wstring> text_;
        std::shared_ptr<void> text_subscription_;
-       binding<int> current_bearing_y_;
-       binding<int> current_protrude_under_y_;
+       variable_impl<int> current_bearing_y_;
+       variable_impl<int> current_protrude_under_y_;
        draw_frame frame_;
        text::texture_atlas atlas_;
        text::texture_font font_;
@@ -153,13 +154,18 @@ public:
                font_.load_glyphs(text::Latin_1_Supplement, text_info.color);
                font_.load_glyphs(text::Latin_Extended_A, text_info.color);
 
-               text_subscription_ = text_.on_change([this]()
+               text_subscription_ = text_.value().on_change([this]()
                {
-                       generate_frame(text_.get());
+                       generate_frame(text_.value().get());
                });
 
+               constraints_.height.depend_on(text());
+               constraints_.width.depend_on(text());
+               current_bearing_y_.as<int>().depend_on(text());
+               current_protrude_under_y_.as<int>().depend_on(text());
+
                //generate frame
-               text_.set(str);
+               text_.value().set(str);
 
                CASPAR_LOG(info) << print() << L" Initialized";
        }
@@ -177,9 +183,10 @@ public:
 
                this->constraints_.width.set(metrics.width);
                this->constraints_.height.set(metrics.height);
-               current_bearing_y_.set(metrics.bearingY);
-               current_protrude_under_y_.set(metrics.protrudeUnderY);
+               current_bearing_y_.value().set(metrics.bearingY);
+               current_protrude_under_y_.value().set(metrics.protrudeUnderY);
                frame_ = core::draw_frame(std::move(frame));
+               frame_.transform().image_transform.fill_translation[1] = static_cast<double>(metrics.bearingY) / static_cast<double>(metrics.height);
        }
 
        text::string_metrics measure_string(const std::wstring& str)
@@ -197,11 +204,34 @@ public:
        boost::unique_future<std::wstring> call(const std::vector<std::wstring>& param)
        {
                std::wstring result;
-               text_.set(param.empty() ? L"" : param[0]);
+               text_.value().set(param.empty() ? L"" : param[0]);
 
                return async(launch::deferred, [=]{return result;});
        }
 
+       variable& get_variable(const std::wstring& name)
+       {
+               if (name == L"text")
+                       return text_;
+               else if (name == L"current_bearing_y")
+                       return current_bearing_y_;
+               else if (name == L"current_protrude_under_y")
+                       return current_protrude_under_y_;
+
+               CASPAR_THROW_EXCEPTION(caspar_exception() << msg_info(L"text_producer does not have a variable called " + name));
+       }
+
+       const std::vector<std::wstring>& get_variables() const
+       {
+               static std::vector<std::wstring> vars =
+                               boost::assign::list_of<std::wstring>
+                                               (L"text")
+                                               (L"current_bearing_y")
+                                               (L"current_protrude_under_y");
+
+               return vars;
+       }
+
        constraints& pixel_constraints()
        {
                return constraints_;
@@ -209,22 +239,22 @@ public:
 
        binding<std::wstring>& text()
        {
-               return text_;
+               return text_.value();
        }
 
        const binding<int>& current_bearing_y() const
        {
-               return current_bearing_y_;
+               return current_bearing_y_.value();
        }
 
        const binding<int>& current_protrude_under_y() const
        {
-               return current_protrude_under_y_;
+               return current_protrude_under_y_.value();
        }
        
        std::wstring print() const
        {
-               return L"text[" + text_.get() + L"]";
+               return L"text[" + text_.value().get() + L"]";
        }
 
        std::wstring name() const
@@ -236,7 +266,7 @@ public:
        {
                boost::property_tree::wptree info;
                info.add(L"type", L"text");
-               info.add(L"text", text_.get());
+               info.add(L"text", text_.value().get());
                return info;
        }
 };
@@ -247,6 +277,8 @@ text_producer::text_producer(const spl::shared_ptr<frame_factory>& frame_factory
 
 draw_frame text_producer::receive_impl() { return impl_->receive_impl(); }
 boost::unique_future<std::wstring> text_producer::call(const std::vector<std::wstring>& param) { return impl_->call(param); }
+variable& text_producer::get_variable(const std::wstring& name) { return impl_->get_variable(name); }
+const std::vector<std::wstring>& text_producer::get_variables() const { return impl_->get_variables(); }
 text::string_metrics text_producer::measure_string(const std::wstring& str) { return impl_->measure_string(str); }
 
 constraints& text_producer::pixel_constraints() { return impl_->pixel_constraints(); }
@@ -285,7 +317,9 @@ spl::shared_ptr<frame_producer> create_text_producer(const spl::shared_ptr<frame
        try_get_color(col_str, col_val);
        text_info.color = core::text::color<float>(col_val);
 
-       return text_producer::create(frame_factory, x, y, params.at(1), text_info, format_desc.width, format_desc.height, true);
+       bool standalone = get_param(L"STANDALONE", params, false);
+
+       return text_producer::create(frame_factory, x, y, params.at(1), text_info, format_desc.width, format_desc.height, standalone);
 }
 
 }}
index 55ea42f4538f2a327702f5936a7425d379b80dfc..022e1be24582faf808d15bc27e266b1110ffbe89 100644 (file)
@@ -53,6 +53,8 @@ public:
        
        draw_frame receive_impl() override;
        boost::unique_future<std::wstring> call(const std::vector<std::wstring>& param) override;
+       variable& get_variable(const std::wstring& name) override;
+       const std::vector<std::wstring>& get_variables() const override;
 
        text::string_metrics measure_string(const std::wstring& str);
 
index 5cc0cc7aeeb2b54ea651e9a7c6a2ade5709aee2d..c8ce6cec5d15ea86692fa38b76d324b8bce918f9 100644 (file)
@@ -26,6 +26,9 @@
 
 #include <boost/lexical_cast.hpp>
 
+#include <common/log.h>
+#include <common/except.h>
+
 #include "binding.h"
 
 namespace caspar { namespace core {
@@ -69,6 +72,12 @@ public:
                return dynamic_cast<variable_impl<T>&>(*this).value();
        }
 
+       template<typename T>
+       const binding<T>& as() const
+       {
+               return dynamic_cast<const variable_impl<T>&>(*this).value();
+       }
+
        virtual void from_string(const std::wstring& raw_value) = 0;
        virtual std::wstring to_string() const = 0;
 private:
@@ -81,6 +90,11 @@ class variable_impl : public variable
 private:
        binding<T> value_;
 public:
+       variable_impl()
+               : variable(L"", true)
+       {
+       }
+
        variable_impl(const std::wstring& expr, bool is_public, T initial_value = T())
                : variable(expr, is_public)
                , value_(initial_value)
@@ -92,9 +106,26 @@ public:
                return value_;
        }
 
+       const binding<T>& value() const
+       {
+               return value_;
+       }
+
        virtual void from_string(const std::wstring& raw_value)
        {
-               value_.set(boost::lexical_cast<T>(raw_value));
+               if (std::is_same<bool, T>::value)
+               {
+                       binding<bool>& bool_binding = *reinterpret_cast<binding<bool>*>(&value_);
+
+                       if (raw_value == L"true")
+                               bool_binding.set(true);
+                       else if (raw_value == L"false")
+                               bool_binding.set(false);
+                       else
+                               CASPAR_THROW_EXCEPTION(caspar_exception() << msg_info(L"bool constants should be true or false"));
+               }
+               else
+                       value_.set(boost::lexical_cast<T>(raw_value));
        }
 
        virtual std::wstring to_string() const
index e0084ee3e384a8f4b68c340b713a5cf25efeced1..43455fbada1527bd8996e9e79ed24dc32d922e33 100644 (file)
@@ -240,10 +240,17 @@ public:
        {
                return false;
        }
+
+       int delay_millis() const
+       {
+               return 60;
+       }
        
        int buffer_depth() const override
        {
-               return 3;
+               int delay_in_frames = static_cast<int>(delay_millis() / (1000.0 / format_desc_.fps));
+               
+               return delay_in_frames;
        }
                
        int index() const override