]> git.sesse.net Git - casparcg/commitdiff
Added basic support for image sequences.
authorHelge Norberg <helge.norberg@svt.se>
Thu, 29 Aug 2013 09:35:59 +0000 (11:35 +0200)
committerHelge Norberg <helge.norberg@svt.se>
Thu, 29 Aug 2013 09:35:59 +0000 (11:35 +0200)
core/producer/scene/const_producer.cpp
core/producer/scene/const_producer.h
core/producer/scene/xml_scene_producer.cpp
core/producer/text/text_producer.cpp
core/producer/text/text_producer.h
core/producer/text/utils/text_info.h
core/producer/text/utils/texture_font.cpp
core/producer/text/utils/texture_font.h
modules/decklink/consumer/decklink_consumer.cpp
modules/image/producer/image_producer.cpp

index 0e86d9d729fc09a47d9ce90ccd63c7bb37b80f79..223263127dd3e08b142443e31c176b9d07e05930 100644 (file)
@@ -30,11 +30,21 @@ namespace caspar { namespace core {
 
 class const_producer : public frame_producer_base
 {
-       draw_frame frame_;
+       std::vector<draw_frame> frames_;
+       std::vector<draw_frame>::const_iterator seek_position_;
        constraints constraints_;
 public:
        const_producer(const draw_frame& frame, int width, int height)
-               : frame_(frame)
+               : constraints_(width, height)
+       {
+               frames_.push_back(frame);
+               seek_position_ = frames_.begin();
+               CASPAR_LOG(info) << print() << L" Initialized";
+       }
+
+       const_producer(std::vector<draw_frame>&& frames, int width, int height)
+               : frames_(std::move(frames))
+               , seek_position_(frames_.begin())
                , constraints_(width, height)
        {
                CASPAR_LOG(info) << print() << L" Initialized";
@@ -42,7 +52,12 @@ public:
 
        draw_frame receive_impl() override
        {
-               return frame_;
+               auto result = *seek_position_;
+
+               if (seek_position_ + 1 != frames_.end())
+                       ++seek_position_;
+
+               return result;
        }
 
        constraints& pixel_constraints() override
@@ -82,4 +97,10 @@ spl::shared_ptr<class frame_producer> create_const_producer(
        return spl::make_shared<const_producer>(frame, width, height);
 }
 
+spl::shared_ptr<class frame_producer> create_const_producer(
+               std::vector<class draw_frame>&& frames, int width, int height)
+{
+       return spl::make_shared<const_producer>(std::move(frames), width, height);
+}
+
 }}
index 4a79f6b90ac16bccbd15bb8f14abfe16777a34c3..a0fdd19255e3ddd97efe16695c1a3162aee3891e 100644 (file)
@@ -30,5 +30,7 @@ namespace caspar { namespace core {
 
 spl::shared_ptr<class frame_producer> create_const_producer(
                const class draw_frame& frame, int width, int height);
+spl::shared_ptr<class frame_producer> create_const_producer(
+               std::vector<class draw_frame>&& frames, int width, int height);
 
 }}
index ab71e6371209269a40238ea293ee8bad906f397c..e68f434ff26934a99f2290664d221bc28c5ce36f 100644 (file)
@@ -128,8 +128,6 @@ spl::shared_ptr<core::frame_producer> create_xml_scene_producer(
                                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>();
                }
        }
 
index 7389a48c266a6c7d322fca27d721265115a6b947..7777cd9b2c790dcea5aca416735c3fe8cb0e37b0 100644 (file)
@@ -130,17 +130,20 @@ namespace caspar { namespace core {
 
 struct text_producer::impl
 {
-       spl::shared_ptr<core::frame_factory>    frame_factory_;
+       spl::shared_ptr<core::frame_factory> frame_factory_;
        constraints constraints_;
        int x_, y_, parent_width_, parent_height_;
        bool standalone_;
        variable_impl<std::wstring> text_;
        std::shared_ptr<void> text_subscription_;
-       variable_impl<int> current_bearing_y_;
-       variable_impl<int> current_protrude_under_y_;
+       variable_impl<double> tracking_;
+       std::shared_ptr<void> tracking_subscription_;
+       variable_impl<double> current_bearing_y_;
+       variable_impl<double> current_protrude_under_y_;
        draw_frame frame_;
        text::texture_atlas atlas_;
        text::texture_font font_;
+       bool dirty_;
 
 public:
        explicit impl(const spl::shared_ptr<frame_factory>& frame_factory, int x, int y, const std::wstring& str, text::text_info& text_info, long parent_width, long parent_height, bool standalone) 
@@ -150,21 +153,27 @@ public:
                , standalone_(standalone)
                , atlas_(512,512,4)
                , font_(atlas_, text::find_font_file(text_info), !standalone)
+               , dirty_(false)
        {
                //TODO: examine str to determine which unicode_blocks to load
                font_.load_glyphs(text::Basic_Latin, text_info.color);
                font_.load_glyphs(text::Latin_1_Supplement, text_info.color);
                font_.load_glyphs(text::Latin_Extended_A, text_info.color);
 
+               tracking_.value().set(text_info.tracking);
                text_subscription_ = text_.value().on_change([this]()
                {
-                       generate_frame(text_.value().get());
+                       dirty_ = true;
+               });
+               tracking_subscription_ = tracking_.value().on_change([this]()
+               {
+                       dirty_ = true;
                });
 
                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());
+               current_bearing_y_.as<double>().depend_on(text());
+               current_protrude_under_y_.as<double>().depend_on(text());
 
                //generate frame
                text_.value().set(str);
@@ -172,13 +181,14 @@ public:
                CASPAR_LOG(info) << print() << L" Initialized";
        }
 
-       void generate_frame(const std::wstring& str)
+       void generate_frame()
        {
                core::pixel_format_desc pfd(core::pixel_format::bgra);
                pfd.planes.push_back(core::pixel_format_desc::plane(static_cast<int>(atlas_.width()), static_cast<int>(atlas_.height()), static_cast<int>(atlas_.depth())));
 
                text::string_metrics metrics;
-               std::vector<float> vertex_stream(std::move(font_.create_vertex_stream(str, x_, y_, parent_width_, parent_height_, &metrics)));
+               font_.set_tracking(static_cast<int>(tracking_.value().get()));
+               std::vector<float> vertex_stream(std::move(font_.create_vertex_stream(text_.value().get(), x_, y_, parent_width_, parent_height_, &metrics)));
                auto frame = frame_factory_->create_frame(vertex_stream.data(), pfd);
                memcpy(frame.image_data().data(), atlas_.data(), frame.image_data().size());
                frame.set_geometry(frame_geometry(frame_geometry::quad_list, std::move(vertex_stream)));
@@ -200,6 +210,9 @@ public:
                        
        draw_frame receive_impl()
        {
+               if (dirty_)
+                       generate_frame();
+
                return frame_;
        }
 
@@ -219,6 +232,8 @@ public:
                        return current_bearing_y_;
                else if (name == L"current_protrude_under_y")
                        return current_protrude_under_y_;
+               else if (name == L"tracking")
+                       return tracking_;
 
                CASPAR_THROW_EXCEPTION(caspar_exception() << msg_info(L"text_producer does not have a variable called " + name));
        }
@@ -228,6 +243,7 @@ public:
                static std::vector<std::wstring> vars =
                                boost::assign::list_of<std::wstring>
                                                (L"text")
+                                               (L"tracking")
                                                (L"current_bearing_y")
                                                (L"current_protrude_under_y");
 
@@ -244,12 +260,17 @@ public:
                return text_.value();
        }
 
-       const binding<int>& current_bearing_y() const
+       binding<double>& tracking()
+       {
+               return tracking_.value();
+       }
+
+       const binding<double>& current_bearing_y() const
        {
                return current_bearing_y_.value();
        }
 
-       const binding<int>& current_protrude_under_y() const
+       const binding<double>& current_protrude_under_y() const
        {
                return current_protrude_under_y_.value();
        }
@@ -290,8 +311,9 @@ boost::property_tree::wptree text_producer::info() const { return impl_->info();
 void text_producer::subscribe(const monitor::observable::observer_ptr& o) {}
 void text_producer::unsubscribe(const monitor::observable::observer_ptr& o) {}
 binding<std::wstring>& text_producer::text() { return impl_->text(); }
-const binding<int>& text_producer::current_bearing_y() const { return impl_->current_bearing_y(); }
-const binding<int>& text_producer::current_protrude_under_y() const { return impl_->current_protrude_under_y(); }
+binding<double>& text_producer::tracking() { return impl_->tracking(); }
+const binding<double>& text_producer::current_bearing_y() const { return impl_->current_bearing_y(); }
+const binding<double>& text_producer::current_protrude_under_y() const { return impl_->current_protrude_under_y(); }
 
 spl::shared_ptr<text_producer> text_producer::create(const spl::shared_ptr<frame_factory>& frame_factory, int x, int y, const std::wstring& str, text::text_info& text_info, long parent_width, long parent_height, bool standalone)
 {
index d0a3f9c4dea414862e821f85614bdf6dbe70a1ed..97e47888f88e8bce79bb65919f811cbeaedfc378 100644 (file)
@@ -60,8 +60,9 @@ public:
        void unsubscribe(const monitor::observable::observer_ptr& o) override;
 
        binding<std::wstring>& text();
-       const binding<int>& current_bearing_y() const;
-       const binding<int>& current_protrude_under_y() const;
+       binding<double>& tracking();
+       const binding<double>& current_bearing_y() const;
+       const binding<double>& current_protrude_under_y() const;
 private:
        struct impl;
        spl::unique_ptr<impl> impl_;
index bc1ade0e567b255fe4796089951c0403b2460e27..0bfc5ea25041aa4c342f014f16feddf76989b4da 100644 (file)
@@ -14,6 +14,13 @@ namespace caspar { namespace core { namespace text {
                color<float> color;
                int baseline_shift;
                int tracking;
+
+               text_info()
+                       : size(0.0f)
+                       , baseline_shift(0)
+                       , tracking(0)
+               {
+               }
        };
 
 }}}
\ No newline at end of file
index 6cd41263dca5be6c94493725cce30f34c7607a78..29b2398b9d35056807ce280dd64e4218b701ebbd 100644 (file)
@@ -82,6 +82,11 @@ public:
                        FT_Done_FreeType(lib_);
        }
 
+       void set_tracking(int tracking)
+       {
+               tracking_ = size_ * tracking / 1000.0f;
+       }
+
        int count_glyphs_in_range(unicode_block block)
        { 
                unicode_range range = get_range(block);
@@ -290,6 +295,7 @@ public:
 
 texture_font::texture_font(texture_atlas& atlas, const text_info& info, bool normalize_coordinates) : impl_(new impl(atlas, info, normalize_coordinates)) {}
 void texture_font::load_glyphs(unicode_block range, const color<float>& col) { impl_->load_glyphs(range, col); }
+void texture_font::set_tracking(int tracking) { impl_->set_tracking(tracking); }
 std::vector<float> texture_font::create_vertex_stream(const std::wstring& str, int x, int y, int parent_width, int parent_height, string_metrics* metrics) { return impl_->create_vertex_stream(str, x, y, parent_width, parent_height, metrics); }
 string_metrics texture_font::measure_string(const std::wstring& str) { return impl_->measure_string(str); }
 
index 8e977d34f2e98d34d9ddb798cf19e10c3e20b0eb..2cfcec6018f927df40f389e879c7ee244c9b6f33 100644 (file)
@@ -21,6 +21,7 @@ class texture_font
 public:
        texture_font(texture_atlas&, const text_info&, bool normalize_coordinates);
        void load_glyphs(unicode_block block, const color<float>& col);
+       void set_tracking(int tracking);
        std::vector<float> create_vertex_stream(const std::wstring& str, int x, int y, int parent_width, int parent_height, string_metrics* metrics);
        string_metrics measure_string(const std::wstring& str);
 
index 97043a03f51de99f30b7d000e1a8558166aee3b6..769ebc07b48a5f418075b2aa0dd16ef70c49a995 100644 (file)
@@ -526,6 +526,7 @@ public:
        {
                executor_.invoke([=]
                {
+                       consumer_.reset();
                        ::CoUninitialize();
                });
        }
index 6b8ea158f8b4773d26f3ed8e859f31923151c662..dcf2323522d45860bd260d1f3d208f2ad3ed1c6f 100644 (file)
@@ -26,6 +26,7 @@
 #include <core/video_format.h>
 
 #include <core/producer/frame_producer.h>
+#include <core/producer/scene/const_producer.h>
 #include <core/frame/frame.h>
 #include <core/frame/draw_frame.h>
 #include <core/frame/frame_factory.h>
@@ -39,6 +40,7 @@
 #include <boost/assign.hpp>
 #include <boost/filesystem.hpp>
 #include <boost/property_tree/ptree.hpp>
+#include <boost/algorithm/string.hpp>
 
 #include <algorithm>
 
@@ -46,6 +48,29 @@ using namespace boost::assign;
 
 namespace caspar { namespace image {
 
+std::pair<core::draw_frame, core::constraints> load_image(
+               const spl::shared_ptr<core::frame_factory>& frame_factory,
+               const std::wstring& filename)
+{
+       auto bitmap = load_image(filename);
+       FreeImage_FlipVertical(bitmap.get());
+               
+       core::pixel_format_desc desc = core::pixel_format::bgra;
+       auto width = FreeImage_GetWidth(bitmap.get());
+       auto height = FreeImage_GetHeight(bitmap.get());
+       desc.planes.push_back(core::pixel_format_desc::plane(width, height, 4));
+       auto frame = frame_factory->create_frame(bitmap.get(), desc);
+
+       std::copy_n(
+                       FreeImage_GetBits(bitmap.get()),
+                       frame.image_data(0).size(),
+                       frame.image_data(0).begin());
+       
+       return std::make_pair(
+                       core::draw_frame(std::move(frame)),
+                       core::constraints(width, height));
+}
+
 struct image_producer : public core::frame_producer_base
 {      
        monitor::basic_subject  event_subject_;
@@ -57,19 +82,10 @@ struct image_producer : public core::frame_producer_base
                : filename_(filename)
                , frame_(core::draw_frame::empty())     
        {
-               auto bitmap = load_image(filename_);
-               FreeImage_FlipVertical(bitmap.get());
-               
-               core::pixel_format_desc desc = core::pixel_format::bgra;
-               auto width = FreeImage_GetWidth(bitmap.get());
-               auto height = FreeImage_GetHeight(bitmap.get());
-               desc.planes.push_back(core::pixel_format_desc::plane(width, height, 4));
-               auto frame = frame_factory->create_frame(this, desc);
+               auto frame = load_image(frame_factory, filename);
 
-               std::copy_n(FreeImage_GetBits(bitmap.get()), frame.image_data(0).size(), frame.image_data(0).begin());
-               frame_ = core::draw_frame(std::move(frame));
-               constraints_.width.set(width);
-               constraints_.height.set(height);
+               frame_ = frame.first;
+               constraints_ = frame.second;
 
                CASPAR_LOG(info) << print() << L" Initialized";
        }
@@ -117,11 +133,76 @@ struct image_producer : public core::frame_producer_base
        }
 };
 
+class ieq
+{
+       std::wstring test_;
+public:
+       ieq(const std::wstring& test)
+               : test_(test)
+       {
+       }
+
+       bool operator()(const std::wstring& elem) const
+       {
+               return boost::iequals(elem, test_);
+       }
+};
+
 spl::shared_ptr<core::frame_producer> create_producer(const spl::shared_ptr<core::frame_factory>& frame_factory, const core::video_format_desc& format_desc, const std::vector<std::wstring>& params)
 {
        static const std::vector<std::wstring> extensions = list_of(L".png")(L".tga")(L".bmp")(L".jpg")(L".jpeg")(L".gif")(L".tiff")(L".tif")(L".jp2")(L".jpx")(L".j2k")(L".j2c");
+
+       if (boost::iequals(params[0], L"[IMG_SEQUENCE]"))
+       {
+               if (params.size() != 2)
+                       return core::frame_producer::empty();
+
+               auto dir = boost::filesystem::path(env::media_folder() + L"\\" + params[1]).parent_path();
+               auto basename = boost::filesystem3::basename(params[1]);
+               std::set<std::wstring> files;
+               boost::filesystem::directory_iterator end;
+
+               for (boost::filesystem::directory_iterator it(dir); it != end; ++it)
+               {
+                       auto name = it->path().filename().wstring();
+
+                       if (!boost::algorithm::istarts_with(name, basename))
+                               continue;
+
+                       auto extension = it->path().extension().wstring();
+
+                       if (std::find_if(extensions.begin(), extensions.end(), ieq(extension)) == extensions.end())
+                               continue;
+
+                       files.insert(it->path().wstring());
+               }
+
+               if (files.empty())
+                       return core::frame_producer::empty();
+
+               int width = -1;
+               int height = -1;
+               std::vector<core::draw_frame> frames;
+               frames.reserve(files.size());
+
+               BOOST_FOREACH(auto& file, files)
+               {
+                       auto frame = load_image(frame_factory, file);
+
+                       if (width == -1)
+                       {
+                               width = static_cast<int>(frame.second.width.get());
+                               height = static_cast<int>(frame.second.height.get());
+                       }
+
+                       frames.push_back(std::move(frame.first));
+               }
+
+               return core::create_const_producer(std::move(frames), width, height);
+       }
+
        std::wstring filename = env::media_folder() + L"\\" + params[0];
-       
+
        auto ext = std::find_if(extensions.begin(), extensions.end(), [&](const std::wstring& ex) -> bool
                {                       
                        return boost::filesystem::is_regular_file(boost::filesystem::path(filename).replace_extension(ex));