- Added thread local contextual information which currently include channel and layer where applicable (used by diag).
- Created graph_sink abstraction, separating diag backend from diag API.
- Bundled liberation fonts for use with SFML font rendering.
- In addition to ordinary diag OSD window backend an OSC backend has also been implemented:
/diag/[graph_id]/text <string> for the name of the graph
/diag/[graph_id]/color/[value-name] <int> for the 32-bit rgba value of the color for the tag or value with the a given name.
/diag/[graph_id]/context/channel <int> for the channel that the graph concerns (if any)
/diag/[graph_id]/context/layer <int> for the layer that the graph concerns (if any)
/diag/[graph_id]/value/[value-name] <float> for the value generally within 0.0 <= n <= 1.0
/diag/[graph_id]/tag/[value-name] only sent when an "event" happens, like a dropped frame or similar. Generally no values are sent for value-names dedicated for tagging.
/clang-analyze.out
*.suo
/dependencies64/boost/stage/lib/libboost_wave-vc120-mt-gd-1_57.lib
+/shell/LiberationSans-Regular.ttf
#include "graph.h"
-#pragma warning (disable : 4244)
+#include <boost/range/adaptor/transformed.hpp>
+#include <boost/thread.hpp>
-#include "../executor.h"
-#include "../lock.h"
-#include "../env.h"
-
-#include <SFML/Graphics.hpp>
-
-#include <boost/optional.hpp>
-#include <boost/circular_buffer.hpp>
-#include <boost/range/algorithm_ext/erase.hpp>
-
-#include <tbb/concurrent_unordered_map.h>
-#include <tbb/atomic.h>
#include <tbb/spin_mutex.h>
-#include <GL/glew.h>
-
-#include <array>
-#include <numeric>
-#include <tuple>
-#include <memory>
+#include <vector>
namespace caspar { namespace diagnostics {
return std::make_tuple(r, g, b, a);
}
-sf::Font& get_default_font()
-{
- static sf::Font DEFAULT_FONT = []()
- {
- sf::Font font;
- if (!font.loadFromFile("arial.ttf"))
- BOOST_THROW_EXCEPTION(caspar_exception() << msg_info("arial.ttf not found"));
- return font;
- }();
-
- return DEFAULT_FONT;
-}
+typedef std::vector<spi::sink_factory_t> sink_factories_t;
+static boost::mutex g_sink_factories_mutex;
+static sink_factories_t g_sink_factories;
-struct drawable : public sf::Drawable, public sf::Transformable
+std::vector<spl::shared_ptr<spi::graph_sink>> create_sinks()
{
- virtual ~drawable(){}
- virtual void render(sf::RenderTarget& target, sf::RenderStates states) = 0;
- virtual void draw(sf::RenderTarget& target, sf::RenderStates states) const override
- {
- states.transform *= getTransform();
- glLoadMatrixf(states.transform.getMatrix());
- const_cast<drawable*>(this)->render(target, states);
- }
-};
-
-class context : public drawable
-{
- std::unique_ptr<sf::RenderWindow> window_;
-
- std::list<std::weak_ptr<drawable>> drawables_;
-
- executor executor_ = L"diagnostics";
-public:
-
- static void register_drawable(const std::shared_ptr<drawable>& drawable)
- {
- if(!drawable)
- return;
-
- get_instance()->executor_.begin_invoke([=]
- {
- get_instance()->do_register_drawable(drawable);
- }, task_priority::high_priority);
- }
-
- static void show(bool value)
- {
- get_instance()->executor_.begin_invoke([=]
- {
- get_instance()->do_show(value);
- }, task_priority::high_priority);
- }
-
- static void shutdown()
- {
- get_instance().reset();
- }
-private:
- context()
- {
- executor_.begin_invoke([=]
- {
- SetThreadPriority(GetCurrentThread(), BELOW_NORMAL_PRIORITY_CLASS);
- });
- }
-
- void do_show(bool value)
- {
- if(value)
- {
- if(!window_)
- {
- window_.reset(new sf::RenderWindow(sf::VideoMode(750, 750), "CasparCG Diagnostics"));
- window_->setPosition(sf::Vector2i(0, 0));
- window_->setActive();
- glEnable(GL_BLEND);
- glEnable(GL_LINE_SMOOTH);
- glHint(GL_LINE_SMOOTH_HINT, GL_NICEST);
- glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
- tick();
- }
- }
- else
- window_.reset();
- }
-
- void tick()
- {
- if(!window_)
- return;
-
- sf::Event e;
- while(window_->pollEvent(e))
- {
- if(e.type == sf::Event::Closed)
- {
- window_.reset();
- return;
- }
- }
- //glClear(GL_COLOR_BUFFER_BIT);
- window_->clear();
- window_->draw(*this);
- window_->display();
- boost::this_thread::sleep(boost::posix_time::milliseconds(10));
- executor_.begin_invoke([this]{tick();});
- }
-
- void render(sf::RenderTarget& target, sf::RenderStates states)
- {
- auto count = std::max<size_t>(8, drawables_.size());
- float target_dy = 1.0f/static_cast<float>(count);
-
- float last_y = 0.0f;
- int n = 0;
- for(auto it = drawables_.begin(); it != drawables_.end(); ++n)
- {
- auto drawable = it->lock();
- if(drawable)
- {
- drawable->setScale(static_cast<float>(window_->getSize().x), static_cast<float>(target_dy*window_->getSize().y));
- float target_y = std::max(last_y, static_cast<float>(n * window_->getSize().y)*target_dy);
- drawable->setPosition(0.0f, target_y);
- target.draw(*drawable, states);
- ++it;
- }
- else
- it = drawables_.erase(it);
- }
- }
-
- void do_register_drawable(const std::shared_ptr<drawable>& drawable)
- {
- drawables_.push_back(drawable);
- auto it = drawables_.begin();
- while(it != drawables_.end())
- {
- if(it->lock())
- ++it;
- else
- it = drawables_.erase(it);
- }
- }
-
- static std::unique_ptr<context>& get_instance()
- {
- static auto impl = std::unique_ptr<context>(new context);
- return impl;
- }
-};
+ boost::lock_guard<boost::mutex> lock(g_sink_factories_mutex);
+ auto sinks = g_sink_factories | boost::adaptors::transformed([](const spi::sink_factory_t& s){ return s(); });
+ return std::vector<spl::shared_ptr<spi::graph_sink>>(std::begin(sinks), std::end(sinks));
+}
-class line : public drawable
+struct graph::impl
{
- boost::circular_buffer<std::pair<float, bool>> line_data_;
-
- tbb::atomic<float> tick_data_;
- tbb::atomic<bool> tick_tag_;
- tbb::atomic<int> color_;
+ std::vector<spl::shared_ptr<spi::graph_sink>> sinks_ = create_sinks();
public:
- line(size_t res = 1200)
- : line_data_(res)
- {
- tick_data_ = -1.0f;
- color_ = 0xFFFFFFFF;
- tick_tag_ = false;
-
- line_data_.push_back(std::make_pair(-1.0f, false));
- }
-
- void set_value(float value)
- {
- tick_data_ = value;
- }
-
- void set_tag()
- {
- tick_tag_ = true;
- }
-
- void set_color(int color)
+ impl()
{
- color_ = color;
}
- int get_color()
+ void activate()
{
- return color_;
+ for (auto& sink : sinks_)
+ sink->activate();
}
-
- void render(sf::RenderTarget& target, sf::RenderStates states)
- {
- float dx = 1.0f/static_cast<float>(line_data_.capacity());
- float x = static_cast<float>(line_data_.capacity()-line_data_.size())*dx;
-
- line_data_.push_back(std::make_pair(tick_data_, tick_tag_));
- tick_tag_ = false;
-
- glBegin(GL_LINE_STRIP);
- auto c = color(color_);
- glColor4f(std::get<0>(c), std::get<1>(c), std::get<2>(c), 0.8f);
- for(size_t n = 0; n < line_data_.size(); ++n)
- if(line_data_[n].first > -0.5)
- glVertex3d(x+n*dx, std::max(0.05, std::min(0.95, (1.0f-line_data_[n].first)*0.8 + 0.1f)), 0.0);
- glEnd();
-
- glEnable(GL_LINE_STIPPLE);
- glLineStipple(3, 0xAAAA);
- for(size_t n = 0; n < line_data_.size(); ++n)
- {
- if(line_data_[n].second)
- {
- glBegin(GL_LINE_STRIP);
- glVertex3f(x+n*dx, 0.0f, 0.0f);
- glVertex3f(x+n*dx, 1.0f, 0.0f);
- glEnd();
- }
- }
- glDisable(GL_LINE_STIPPLE);
- }
-};
-struct graph::impl : public drawable
-{
- tbb::concurrent_unordered_map<std::string, diagnostics::line> lines_;
-
- tbb::spin_mutex mutex_;
- std::wstring text_;
- bool auto_reset_ = false;
-
- impl()
- {
- }
-
void set_text(const std::wstring& value)
{
- auto temp = value;
- lock(mutex_, [&]
- {
- text_ = std::move(temp);
- });
+ for (auto& sink : sinks_)
+ sink->set_text(value);
}
void set_value(const std::string& name, double value)
{
- lines_[name].set_value(value);
+ for (auto& sink : sinks_)
+ sink->set_value(name, value);
}
void set_tag(const std::string& name)
{
- lines_[name].set_tag();
+ for (auto& sink : sinks_)
+ sink->set_tag(name);
}
void set_color(const std::string& name, int color)
{
- lines_[name].set_color(color);
+ for (auto& sink : sinks_)
+ sink->set_color(name, color);
}
+
void auto_reset()
{
- lock(mutex_, [this]
- {
- auto_reset_ = true;
- });
+ for (auto& sink : sinks_)
+ sink->auto_reset();
}
private:
- void render(sf::RenderTarget& target, sf::RenderStates states)
- {
- const size_t text_size = 15;
- const size_t text_margin = 2;
- const size_t text_offset = (text_size+text_margin*2)*2;
-
- std::wstring text_str;
- bool auto_reset;
-
- {
- tbb::spin_mutex::scoped_lock lock(mutex_);
- text_str = text_;
- auto_reset = auto_reset_;
- }
-
- //sf::Text test("Test", get_default_font());
- //target.draw(test, states);
-
- sf::Text text(text_str.c_str(), get_default_font(), text_size);
- text.setStyle(sf::Text::Italic);
- text.move(text_margin, text_margin);
-
- //glPushMatrix();
- //glScaled(1.0f/getScale().x, 1.0f/getScale().y, 1.0f);
- target.draw(text, states);
- float x_offset = text_margin;
- for(auto it = lines_.begin(); it != lines_.end(); ++it)
- {
- sf::Text line_text(it->first, get_default_font(), text_size);
- line_text.setPosition(x_offset, text_margin+text_offset/2);
- auto c = it->second.get_color();
- line_text.setColor(sf::Color((c >> 24) & 255, (c >> 16) & 255, (c >> 8) & 255, (c >> 0) & 255));
- target.draw(line_text, states);
- x_offset += (line_text.getLocalBounds().width/* - line_text.getLocalBounds().left*/) + text_margin * 2;
- }
-
- glDisable(GL_TEXTURE_2D);
- //glPopMatrix();
-
- static auto rect = []()
- {
- sf::RectangleShape r(sf::Vector2f(1.0f, 0.99f));
- r.setFillColor(sf::Color(255, 255, 255, 51));
- r.setOutlineThickness(0.00f);
- return r;
- }();
- target.draw(rect, states);
- /*glBegin(GL_QUADS);
- glColor4f(1.0f, 1.0f, 1.0f, 0.2f);
- glVertex2f(1.0f, 0.99f);
- glVertex2f(0.0f, 0.99f);
- glVertex2f(0.0f, 0.01f);
- glVertex2f(1.0f, 0.01f);
- glEnd();*/
-
- states.transform
- .translate(0, text_offset)
- .scale(1.0f, text_offset / getScale().y);
- //glLoadMatrixf(states.transform.getMatrix());
- //glPushMatrix();
- //glTranslated(0.0f, text_offset/getScale().y, 1.0f);
- //glScaled(1.0f, 1.0-text_offset/getScale().y, 1.0f);
-
- glEnable(GL_LINE_STIPPLE);
- glLineStipple(3, 0xAAAA);
- glColor4f(1.0f, 1.0f, 1.9f, 0.5f);
- glBegin(GL_LINE_STRIP);
- glVertex3f(0.0f, (1.0f-0.5f) * 0.8f + 0.1f, 0.0f);
- glVertex3f(1.0f, (1.0f-0.5f) * 0.8f + 0.1f, 0.0f);
- glEnd();
- glBegin(GL_LINE_STRIP);
- glVertex3f(0.0f, (1.0f-0.0f) * 0.8f + 0.1f, 0.0f);
- glVertex3f(1.0f, (1.0f-0.0f) * 0.8f + 0.1f, 0.0f);
- glEnd();
- glBegin(GL_LINE_STRIP);
- glVertex3f(0.0f, (1.0f-1.0f) * 0.8f + 0.1f, 0.0f);
- glVertex3f(1.0f, (1.0f-1.0f) * 0.8f + 0.1f, 0.0f);
- glEnd();
- glDisable(GL_LINE_STIPPLE);
-
- //target.Draw(diagnostics::guide(1.0f, color(1.0f, 1.0f, 1.0f, 0.6f)));
- //target.Draw(diagnostics::guide(0.0f, color(1.0f, 1.0f, 1.0f, 0.6f)));
-
- for(auto it = lines_.begin(); it != lines_.end(); ++it)
- {
- target.draw(it->second, states);
- if(auto_reset)
- it->second.set_value(0.0f);
- }
-
- //glPopMatrix();
- }
-
impl(impl&);
impl& operator=(impl&);
};
-graph::graph() : impl_(new impl())
+graph::graph() : impl_(new impl)
{
}
-void graph::set_text(const std::wstring& value){impl_->set_text(value);}
-void graph::set_value(const std::string& name, double value){impl_->set_value(name, value);}
-void graph::set_color(const std::string& name, int color){impl_->set_color(name, color);}
-void graph::set_tag(const std::string& name){impl_->set_tag(name);}
-void graph::auto_reset(){impl_->auto_reset(); }
+void graph::set_text(const std::wstring& value) { impl_->set_text(value); }
+void graph::set_value(const std::string& name, double value) { impl_->set_value(name, value); }
+void graph::set_color(const std::string& name, int color) { impl_->set_color(name, color); }
+void graph::set_tag(const std::string& name) { impl_->set_tag(name); }
+void graph::auto_reset() { impl_->auto_reset(); }
+
void register_graph(const spl::shared_ptr<graph>& graph)
{
- context::register_drawable(graph->impl_);
+ graph->impl_->activate();
}
-void show_graphs(bool value)
-{
- context::show(value);
-}
+namespace spi {
-void shutdown()
+void register_sink_factory(sink_factory_t factory)
{
- context::shutdown();
+ boost::lock_guard<boost::mutex> lock(g_sink_factories_mutex);
+
+ g_sink_factories.push_back(std::move(factory));
}
-//namespace v2
-//{
-//
-//struct line::impl
-//{
-// std::wstring name_;
-// boost::circular_buffer<data> ticks_;
-//
-// impl(const std::wstring& name)
-// : name_(name)
-// , ticks_(1024){}
-//
-// void set_value(float value)
-// {
-// ticks_.push_back();
-// ticks_.back().value = value;
-// }
-//
-// void set_value(float value)
-// {
-// ticks_.clear();
-// set_value(value);
-// }
-//};
-//
-//line::line(){}
-//line::line(const std::wstring& name) : impl_(new impl(name)){}
-//std::wstring line::print() const {return impl_->name_;}
-//void line::set_value(float value){impl_->set_value(value);}
-//void line::set_value(float value){impl_->set_value(value);}
-//boost::circular_buffer<data>& line::ticks() { return impl_->ticks_;}
-//
-//struct graph::impl
-//{
-// std::map<std::wstring, line> lines_;
-// color color_;
-// printer printer_;
-//
-// impl(const std::wstring& name)
-// : printer_([=]{return name;}){}
-//
-// impl(const printer& parent_printer)
-// : printer_(parent_printer){}
-//
-// void set_value(const std::wstring& name, float value)
-// {
-// auto it = lines_.find(name);
-// if(it == lines_.end())
-// it = lines_.insert(std::make_pair(name, line(name))).first;
-//
-// it->second.set_value(value);
-// }
-//
-// void set_value(const std::wstring& name, float value)
-// {
-// auto it = lines_.find(name);
-// if(it == lines_.end())
-// it = lines_.insert(std::make_pair(name, line(name))).first;
-//
-// it->second.set_value(value);
-// }
-//
-// void set_color(const std::wstring& name, color color)
-// {
-// color_ = color;
-// }
-//
-// std::map<std::wstring, line>& get_lines()
-// {
-// return lines_;
-// }
-//
-// color get_color() const
-// {
-// return color_;
-// }
-//
-// std::wstring print() const
-// {
-// return printer_ ? printer_() : L"graph";
-// }
-//};
-//
-//graph::graph(const std::wstring& name) : impl_(new impl(name)){}
-//graph::graph(const printer& parent_printer) : impl_(new impl(parent_printer)){}
-//void graph::set_value(const std::wstring& name, float value){impl_->set_value(name, value);}
-//void graph::set_value(const std::wstring& name, float value){impl_->set_value(name, value);}
-//void graph::set_color(const std::wstring& name, color c){impl_->set_color(name, c);}
-//color graph::get_color() const {return impl_->get_color();}
-//std::wstring graph::print() const {return impl_->print();}
-//
-//spl::shared_ptr<graph> graph::clone() const
-//{
-// spl::shared_ptr<graph> clone(new graph(std::wstring(L"")));
-// clone->impl_->printer_ = impl_->printer_;
-// clone->impl_->lines_ = impl_->lines_;
-// clone->impl_->color_ = impl_->color_;
-//}
-//
-//std::map<std::wstring, line>& graph::get_lines() {impl_->get_lines();}
-//
-//std::vector<spl::shared_ptr<graph>> g_graphs;
-//
-//spl::shared_ptr<graph> create_graph(const std::string& name)
-//{
-// g_graphs.push_back(spl::make_shared<graph>(name));
-// return g_graphs.back();
-//}
-//
-//spl::shared_ptr<graph> create_graph(const printer& parent_printer)
-//{
-// g_graphs.push_back(spl::make_shared<graph>(parent_printer));
-// return g_graphs.back();
-//}
-//
-//static std::vector<spl::shared_ptr<graph>> get_all_graphs()
-//{
-// std::vector<spl::shared_ptr<graph>> graphs;
-// BOOST_FOREACH(auto& graph, g_graphs)
-// graphs.push_back(graph->clone());
-//
-// return graphs;
-//}
-//
-//}
+}
}}
\ No newline at end of file
#include <string>
#include <tuple>
+#include <functional>
#include <boost/noncopyable.hpp>
};
void register_graph(const spl::shared_ptr<graph>& graph);
-void show_graphs(bool value);
-void shutdown();
+
+namespace spi {
+
+class graph_sink : boost::noncopyable
+{
+public:
+ virtual ~graph_sink() { }
+ virtual void activate() = 0;
+ virtual void set_text(const std::wstring& value) = 0;
+ virtual void set_value(const std::string& name, double value) = 0;
+ virtual void set_color(const std::string& name, int color) = 0;
+ virtual void set_tag(const std::string& name) = 0;
+ virtual void auto_reset() = 0;
+};
+
+typedef std::function<spl::shared_ptr<graph_sink>()> sink_factory_t;
+void register_sink_factory(sink_factory_t factory);
+
+}
}}
\ No newline at end of file
// Author: Ryan M. Geiss
// http://www.geisswerks.com/ryan/FAQS/timing.html
void prec_timer::tick(double interval)
-{
+{
+ tick_millis(static_cast<int64_t>(interval * 1000.0));
+}
+
+void prec_timer::tick_millis(int64_t ticks_to_wait)
+{
auto t = ::timeGetTime();
if (time_ != 0)
{
- auto ticks_to_wait = static_cast<DWORD>(interval*1000.0);
bool done = 0;
do
- {
+ {
auto ticks_passed = t - time_;
- auto ticks_left = ticks_to_wait - ticks_passed;
+ auto ticks_left = ticks_to_wait - ticks_passed;
if (t < time_) // time wrap
done = 1;
if (ticks_passed >= ticks_to_wait)
done = 1;
-
+
if (!done)
{
// if > 0.002s left, do Sleep(1), which will actually sleep some
// amount of time.
if (ticks_left > 2)
Sleep(1);
- else
- for (int i = 0; i < 10; ++i)
+ else
+ for (int i = 0; i < 10; ++i)
Sleep(0); // causes thread to give up its timeslice
}
t = ::timeGetTime();
- }
- while (!done);
+ } while (!done);
}
time_ = t;
-}
+}
}
\ No newline at end of file
#pragma once
+#include <cstdint>
+
namespace caspar {
class prec_timer
// Author: Ryan M. Geiss
// http://www.geisswerks.com/ryan/FAQS/timing.html
void tick(double interval);
+ void tick_millis(int64_t interval);
private:
unsigned long time_;
</ItemDefinitionGroup>\r
<ItemGroup>\r
<ClInclude Include="consumer\port.h" />\r
+ <ClInclude Include="diagnostics\call_context.h" />\r
+ <ClInclude Include="diagnostics\osd_graph.h" />\r
+ <ClInclude Include="diagnostics\subject_diagnostics.h" />\r
<ClInclude Include="frame\draw_frame.h" />\r
<ClInclude Include="frame\frame.h" />\r
<ClInclude Include="frame\frame_factory.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="diagnostics\call_context.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="diagnostics\osd_graph.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="diagnostics\subject_diagnostics.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="frame\frame.cpp">\r
<PrecompiledHeaderFile Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">../StdAfx.h</PrecompiledHeaderFile>\r
<PrecompiledHeaderFile Condition="'$(Configuration)|$(Platform)'=='Release|x64'">../StdAfx.h</PrecompiledHeaderFile>\r
<Filter Include="source\producer\text\utils">\r
<UniqueIdentifier>{fc435676-34ca-46fe-bbcf-2fbad8e3cd21}</UniqueIdentifier>\r
</Filter>\r
+ <Filter Include="source\diagnostics">\r
+ <UniqueIdentifier>{42cbb33e-a2fd-43a1-86fb-204caa2afc13}</UniqueIdentifier>\r
+ </Filter>\r
</ItemGroup>\r
<ItemGroup>\r
<ClInclude Include="producer\transition\transition_producer.h">\r
<ClInclude Include="thumbnail_generator.h">\r
<Filter>source</Filter>\r
</ClInclude>\r
+ <ClInclude Include="diagnostics\subject_diagnostics.h">\r
+ <Filter>source\diagnostics</Filter>\r
+ </ClInclude>\r
+ <ClInclude Include="diagnostics\call_context.h">\r
+ <Filter>source\diagnostics</Filter>\r
+ </ClInclude>\r
+ <ClInclude Include="diagnostics\osd_graph.h">\r
+ <Filter>source\diagnostics</Filter>\r
+ </ClInclude>\r
</ItemGroup>\r
<ItemGroup>\r
<ClCompile Include="producer\transition\transition_producer.cpp">\r
<ClCompile Include="thumbnail_generator.cpp">\r
<Filter>source</Filter>\r
</ClCompile>\r
+ <ClCompile Include="diagnostics\subject_diagnostics.cpp">\r
+ <Filter>source\diagnostics</Filter>\r
+ </ClCompile>\r
+ <ClCompile Include="diagnostics\call_context.cpp">\r
+ <Filter>source\diagnostics</Filter>\r
+ </ClCompile>\r
+ <ClCompile Include="diagnostics\osd_graph.cpp">\r
+ <Filter>source\diagnostics</Filter>\r
+ </ClCompile>\r
</ItemGroup>\r
</Project>
\ No newline at end of file
--- /dev/null
+/*
+* Copyright (c) 2011 Sveriges Television AB <info@casparcg.com>
+*
+* This file is part of CasparCG (www.casparcg.com).
+*
+* CasparCG is free software: you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation, either version 3 of the License, or
+* (at your option) any later version.
+*
+* CasparCG is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with CasparCG. If not, see <http://www.gnu.org/licenses/>.
+*
+* Author: Helge Norberg, helge.norberg@svt.se
+*/
+
+#include "../StdAfx.h"
+
+#include "call_context.h"
+
+#include <boost/thread/tss.hpp>
+
+namespace caspar { namespace core { namespace diagnostics {
+
+call_context& call_context::for_thread()
+{
+ static boost::thread_specific_ptr<call_context> contexts;
+
+ auto local = contexts.get();
+
+ if (!local)
+ {
+ local = new call_context;
+ contexts.reset(local);
+ }
+
+ return *local;
+}
+
+}}}
--- /dev/null
+/*
+* Copyright (c) 2011 Sveriges Television AB <info@casparcg.com>
+*
+* This file is part of CasparCG (www.casparcg.com).
+*
+* CasparCG is free software: you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation, either version 3 of the License, or
+* (at your option) any later version.
+*
+* CasparCG is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with CasparCG. If not, see <http://www.gnu.org/licenses/>.
+*
+* Author: Helge Norberg, helge.norberg@svt.se
+*/
+
+#pragma once
+
+namespace caspar { namespace core { namespace diagnostics {
+
+struct call_context
+{
+ int video_channel = -1;
+ int layer = -1;
+
+ static call_context& for_thread();
+};
+
+class scoped_call_context : boost::noncopyable
+{
+ call_context saved_ = call_context::for_thread();
+public:
+ ~scoped_call_context()
+ {
+ call_context::for_thread() = saved_;
+ }
+};
+
+}}}
--- /dev/null
+/*
+* Copyright (c) 2011 Sveriges Television AB <info@casparcg.com>
+*
+* This file is part of CasparCG (www.casparcg.com).
+*
+* CasparCG is free software: you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation, either version 3 of the License, or
+* (at your option) any later version.
+*
+* CasparCG is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with CasparCG. If not, see <http://www.gnu.org/licenses/>.
+*
+* Author: Robert Nagy, ronag89@gmail.com
+*/
+
+#include "../stdafx.h"
+
+#include "osd_graph.h"
+
+#pragma warning (disable : 4244)
+
+#include "call_context.h"
+
+#include <common/executor.h>
+#include <common/lock.h>
+#include <common/env.h>
+#include <common/prec_timer.h>
+
+#include <SFML/Graphics.hpp>
+
+#include <boost/optional.hpp>
+#include <boost/circular_buffer.hpp>
+#include <boost/lexical_cast.hpp>
+
+#include <tbb/concurrent_unordered_map.h>
+#include <tbb/atomic.h>
+#include <tbb/spin_mutex.h>
+
+#include <GL/glew.h>
+
+#include <numeric>
+#include <tuple>
+#include <memory>
+
+#include <Windows.h>
+
+namespace caspar { namespace core { namespace diagnostics { namespace osd {
+
+static const int PREFERRED_VERTICAL_GRAPHS = 8;
+static const int RENDERING_WIDTH = 750;
+static const int RENDERING_HEIGHT = RENDERING_WIDTH / PREFERRED_VERTICAL_GRAPHS;
+
+sf::Color get_sfml_color(int color)
+{
+ auto c = caspar::diagnostics::color(color);
+
+ return {
+ (color >> 24) & 255,
+ (color >> 16) & 255,
+ (color >> 8) & 255,
+ (color >> 0) & 255
+ };
+}
+
+sf::Font& get_default_font()
+{
+ static sf::Font DEFAULT_FONT = []()
+ {
+ sf::Font font;
+ if (!font.loadFromFile("LiberationSans-Regular.ttf"))
+ BOOST_THROW_EXCEPTION(caspar_exception() << msg_info("LiberationSans-Regular.ttf not found"));
+ return font;
+ }();
+
+ return DEFAULT_FONT;
+}
+
+struct drawable : public sf::Drawable, public sf::Transformable
+{
+ virtual ~drawable(){}
+ virtual void render(sf::RenderTarget& target, sf::RenderStates states) = 0;
+ virtual void draw(sf::RenderTarget& target, sf::RenderStates states) const override
+ {
+ states.transform *= getTransform();
+ const_cast<drawable*>(this)->render(target, states);
+ }
+};
+
+class context : public drawable
+{
+ std::unique_ptr<sf::RenderWindow> window_;
+ sf::View view_;
+
+ std::list<std::weak_ptr<drawable>> drawables_;
+ int64_t refresh_rate_millis_ = 16;
+ boost::timer display_time_;
+ bool calculate_view_;
+ int scroll_position_ = 0;
+ bool dragging_ = false;
+ int last_mouse_y_;
+
+ executor executor_ = L"diagnostics";
+public:
+
+ static void register_drawable(const std::shared_ptr<drawable>& drawable)
+ {
+ if(!drawable)
+ return;
+
+ get_instance()->executor_.begin_invoke([=]
+ {
+ get_instance()->do_register_drawable(drawable);
+ }, task_priority::high_priority);
+ }
+
+ static void show(bool value)
+ {
+ get_instance()->executor_.begin_invoke([=]
+ {
+ get_instance()->do_show(value);
+ }, task_priority::high_priority);
+ }
+
+ static void shutdown()
+ {
+ get_instance().reset();
+ }
+private:
+ context()
+ {
+ executor_.begin_invoke([=]
+ {
+ SetThreadPriority(GetCurrentThread(), BELOW_NORMAL_PRIORITY_CLASS);
+ });
+ }
+
+ void do_show(bool value)
+ {
+ if(value)
+ {
+ if(!window_)
+ {
+ window_.reset(new sf::RenderWindow(sf::VideoMode(RENDERING_WIDTH, RENDERING_WIDTH), "CasparCG Diagnostics"));
+ window_->setPosition(sf::Vector2i(0, 0));
+ window_->setActive();
+ window_->setVerticalSyncEnabled(true);
+ calculate_view_ = true;
+ glEnable(GL_BLEND);
+ glEnable(GL_LINE_SMOOTH);
+ glHint(GL_LINE_SMOOTH_HINT, GL_NICEST);
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+
+ tick();
+ }
+ }
+ else
+ window_.reset();
+ }
+
+ void tick()
+ {
+ if(!window_)
+ return;
+
+ sf::Event e;
+ while (window_->pollEvent(e))
+ {
+ switch (e.type)
+ {
+ case sf::Event::Closed:
+ window_.reset();
+ return;
+ case sf::Event::Resized:
+ calculate_view_ = true;
+ break;
+ case sf::Event::MouseButtonPressed:
+ dragging_ = true;
+ last_mouse_y_ = e.mouseButton.y;
+ break;
+ case sf::Event::MouseButtonReleased:
+ dragging_ = false;
+ break;
+ case sf::Event::MouseMoved:
+ if (dragging_)
+ {
+ auto delta_y = e.mouseMove.y - last_mouse_y_;
+ scroll_position_ += delta_y;
+ last_mouse_y_ = e.mouseMove.y;
+ calculate_view_ = true;
+ }
+
+ break;
+ case sf::Event::MouseWheelMoved:
+ scroll_position_ += e.mouseWheel.delta * 15;
+ calculate_view_ = true;
+ break;
+ }
+ }
+
+ window_->clear();
+
+ if (calculate_view_)
+ {
+ view_.setViewport(sf::FloatRect(0, 0, 1.0, 1.0));
+ view_.setSize(RENDERING_WIDTH, window_->getSize().y);
+ view_.setCenter(RENDERING_WIDTH / 2, window_->getSize().y / 2 - scroll_position_);
+ window_->setView(view_);
+ calculate_view_ = false;
+ }
+
+ window_->draw(*this);
+
+ static const auto THRESHOLD = 1;
+ int64_t since_last_refresh = display_time_.elapsed() * 1000;
+ int64_t until_next_refresh = refresh_rate_millis_ - since_last_refresh;
+ int64_t sleep_for = until_next_refresh - THRESHOLD;
+
+ if (sleep_for > 0)
+ {
+ prec_timer timer;
+ timer.tick_millis(0);
+ timer.tick_millis(sleep_for);
+ }
+
+ window_->display();
+ display_time_.restart();
+ executor_.begin_invoke([this]{tick();});
+ }
+
+ void render(sf::RenderTarget& target, sf::RenderStates states)
+ {
+ int n = 0;
+
+ for (auto it = drawables_.begin(); it != drawables_.end(); ++n)
+ {
+ auto drawable = it->lock();
+ if (drawable)
+ {
+ float target_y = n * RENDERING_HEIGHT;
+ drawable->setPosition(0.0f, target_y);
+ target.draw(*drawable, states);
+ ++it;
+ }
+ else
+ it = drawables_.erase(it);
+ }
+ }
+
+ void do_register_drawable(const std::shared_ptr<drawable>& drawable)
+ {
+ drawables_.push_back(drawable);
+ auto it = drawables_.begin();
+ while(it != drawables_.end())
+ {
+ if(it->lock())
+ ++it;
+ else
+ it = drawables_.erase(it);
+ }
+ }
+
+ static std::unique_ptr<context>& get_instance()
+ {
+ static auto impl = std::unique_ptr<context>(new context);
+ return impl;
+ }
+};
+
+class line : public drawable
+{
+ size_t res_;
+ boost::circular_buffer<sf::Vertex> line_data_ { res_ };
+ boost::circular_buffer<boost::optional<sf::VertexArray>> line_tags_ { res_ };
+
+ tbb::atomic<float> tick_data_;
+ tbb::atomic<bool> tick_tag_;
+ tbb::atomic<int> color_;
+
+ double x_delta_ = 1.0 / (res_ - 1);
+ //double x_pos_ = 1.0;
+public:
+ line(size_t res = 750)
+ : res_(res)
+ {
+ tick_data_ = -1.0f;
+ color_ = 0xFFFFFFFF;
+ tick_tag_ = false;
+ }
+
+ void set_value(float value)
+ {
+ tick_data_ = value;
+ }
+
+ void set_tag()
+ {
+ tick_tag_ = true;
+ }
+
+ void set_color(int color)
+ {
+ color_ = color;
+ }
+
+ int get_color()
+ {
+ return color_;
+ }
+
+ void render(sf::RenderTarget& target, sf::RenderStates states)
+ {
+ /*states.transform.translate(x_pos_, 0.f);
+
+ if (line_data_.size() == res_)
+ x_pos_ = -get_insertion_xcoord() + 1.0 - x_delta_; // Otherwise the graph will drift because of floating point precision
+ else
+ x_pos_ -= x_delta_;*/
+
+ for (auto& vertex : line_data_)
+ vertex.position.x -= x_delta_;
+
+ auto color = get_sfml_color(color_);
+ color.a = 255 * 0.8;
+ line_data_.push_back(sf::Vertex(sf::Vector2f(get_insertion_xcoord(), std::max(0.05, std::min(0.95, (1.0f - tick_data_) * 0.8 + 0.1f))), color));
+
+ if (tick_tag_)
+ {
+ sf::VertexArray vertical_dash(sf::LinesStrip);
+ vertical_dash.append(sf::Vertex(sf::Vector2f(get_insertion_xcoord() - x_delta_, 0.f), color));
+ vertical_dash.append(sf::Vertex(sf::Vector2f(get_insertion_xcoord() - x_delta_, 1.f), color));
+ line_tags_.push_back(vertical_dash);
+ }
+ else
+ line_tags_.push_back(boost::none);
+
+ tick_tag_ = false;
+
+ if (tick_data_ > -0.5)
+ {
+ auto array_one = line_data_.array_one();
+ auto array_two = line_data_.array_two();
+ // since boost::circular_buffer guarantees two contigous views of the buffer we can provide raw access to SFML, which can use glDrawArrays.
+ target.draw(array_one.first, static_cast<unsigned int>(array_one.second), sf::LinesStrip, states);
+ target.draw(array_two.first, static_cast<unsigned int>(array_two.second), sf::LinesStrip, states);
+
+ if (array_one.second > 0 && array_two.second > 0)
+ {
+ // Connect the gap between the arrays
+ sf::VertexArray connecting_line(sf::LinesStrip);
+ connecting_line.append(*(array_one.first + array_one.second - 1));
+ connecting_line.append(*(array_two.first));
+ target.draw(connecting_line, states);
+ }
+ }
+ else
+ {
+ glEnable(GL_LINE_STIPPLE);
+ glLineStipple(3, 0xAAAA);
+
+ for (size_t n = 0; n < line_tags_.size(); ++n)
+ {
+ if (line_tags_[n])
+ {
+ target.draw(*line_tags_[n], states);
+ }
+ }
+
+ glDisable(GL_LINE_STIPPLE);
+ }
+ }
+private:
+ double get_insertion_xcoord() const
+ {
+ return line_data_.empty() ? 1.0 : line_data_.back().position.x + x_delta_;
+ }
+};
+
+struct graph : public drawable, public caspar::diagnostics::spi::graph_sink, public std::enable_shared_from_this<graph>
+{
+ call_context context_ = call_context::for_thread();
+ tbb::concurrent_unordered_map<std::string, line> lines_;
+
+ tbb::spin_mutex mutex_;
+ std::wstring text_;
+ bool auto_reset_ = false;
+
+ graph()
+ {
+ }
+
+ void activate() override
+ {
+ context::register_drawable(shared_from_this());
+ }
+
+ void set_text(const std::wstring& value) override
+ {
+ auto temp = value;
+ lock(mutex_, [&]
+ {
+ text_ = std::move(temp);
+ });
+ }
+
+ void set_value(const std::string& name, double value) override
+ {
+ lines_[name].set_value(value);
+ }
+
+ void set_tag(const std::string& name) override
+ {
+ lines_[name].set_tag();
+ }
+
+ void set_color(const std::string& name, int color) override
+ {
+ lines_[name].set_color(color);
+ }
+
+ void auto_reset() override
+ {
+ lock(mutex_, [this]
+ {
+ auto_reset_ = true;
+ });
+ }
+
+private:
+ void render(sf::RenderTarget& target, sf::RenderStates states) override
+ {
+ const size_t text_size = 15;
+ const size_t text_margin = 2;
+ const size_t text_offset = (text_size+text_margin*2)*2;
+
+ std::wstring text_str;
+ bool auto_reset;
+
+ {
+ tbb::spin_mutex::scoped_lock lock(mutex_);
+ text_str = text_;
+ auto_reset = auto_reset_;
+ }
+
+ sf::Text text(text_str.c_str(), get_default_font(), text_size);
+ text.setStyle(sf::Text::Italic);
+ text.move(text_margin, text_margin);
+
+ target.draw(text, states);
+
+ if (context_.video_channel != -1)
+ {
+ auto ctx_str = boost::lexical_cast<std::string>(context_.video_channel);
+
+ if (context_.layer != -1)
+ ctx_str += "-" + boost::lexical_cast<std::string>(context_.layer);
+
+ sf::Text context_text(ctx_str, get_default_font(), text_size);
+ context_text.setStyle(sf::Text::Italic);
+ context_text.move(RENDERING_WIDTH - text_margin - 5 - context_text.getLocalBounds().width, text_margin);
+
+ target.draw(context_text, states);
+ }
+
+ float x_offset = text_margin;
+
+ for(auto it = lines_.begin(); it != lines_.end(); ++it)
+ {
+ sf::Text line_text(it->first, get_default_font(), text_size);
+ line_text.setPosition(x_offset, text_margin+text_offset/2);
+ line_text.setColor(get_sfml_color(it->second.get_color()));
+ target.draw(line_text, states);
+ x_offset += line_text.getLocalBounds().width + text_margin * 2;
+ }
+
+ static const auto rect = []()
+ {
+ sf::RectangleShape r(sf::Vector2f(RENDERING_WIDTH, RENDERING_HEIGHT - 2));
+ r.setFillColor(sf::Color(255, 255, 255, 51));
+ r.setOutlineThickness(0.00f);
+ r.move(0, 1);
+ return r;
+ }();
+ target.draw(rect, states);
+
+ states.transform
+ .translate(0, text_offset)
+ .scale(RENDERING_WIDTH, RENDERING_HEIGHT * (static_cast<float>(RENDERING_HEIGHT - text_offset) / static_cast<float>(RENDERING_HEIGHT)));
+
+ static const sf::Color guide_color(255, 255, 255, 127);
+ static const sf::VertexArray middle_guide = []()
+ {
+ sf::VertexArray result(sf::LinesStrip);
+ result.append(sf::Vertex(sf::Vector2f(0.0f, 0.5f), guide_color));
+ result.append(sf::Vertex(sf::Vector2f(1.0f, 0.5f), guide_color));
+ return result;
+ }();
+ static const sf::VertexArray bottom_guide = []()
+ {
+ sf::VertexArray result(sf::LinesStrip);
+ result.append(sf::Vertex(sf::Vector2f(0.0f, 0.9f), guide_color));
+ result.append(sf::Vertex(sf::Vector2f(1.0f, 0.9f), guide_color));
+ return result;
+ }();
+ static const sf::VertexArray top_guide = []()
+ {
+ sf::VertexArray result(sf::LinesStrip);
+ result.append(sf::Vertex(sf::Vector2f(0.0f, 0.1f), guide_color));
+ result.append(sf::Vertex(sf::Vector2f(1.0f, 0.1f), guide_color));
+ return result;
+ }();
+
+ glEnable(GL_LINE_STIPPLE);
+ glLineStipple(3, 0xAAAA);
+
+ target.draw(middle_guide, states);
+ target.draw(bottom_guide, states);
+ target.draw(top_guide, states);
+
+ glDisable(GL_LINE_STIPPLE);
+
+ for (auto it = lines_.begin(); it != lines_.end(); ++it)
+ {
+ target.draw(it->second, states);
+ if(auto_reset)
+ it->second.set_value(0.0f);
+ }
+ }
+};
+
+void register_sink()
+{
+ caspar::diagnostics::spi::register_sink_factory([]
+ {
+ return spl::make_shared<graph>();
+ });
+}
+
+void show_graphs(bool value)
+{
+ context::show(value);
+}
+
+void shutdown()
+{
+ context::shutdown();
+}
+
+}}}}
--- /dev/null
+/*
+* Copyright (c) 2011 Sveriges Television AB <info@casparcg.com>
+*
+* This file is part of CasparCG (www.casparcg.com).
+*
+* CasparCG is free software: you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation, either version 3 of the License, or
+* (at your option) any later version.
+*
+* CasparCG is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with CasparCG. If not, see <http://www.gnu.org/licenses/>.
+*
+* Author: Robert Nagy, ronag89@gmail.com
+*/
+
+#pragma once
+
+#include <common/memory.h>
+#include <common/diagnostics/graph.h>
+
+#include <string>
+#include <tuple>
+#include <functional>
+
+#include <boost/noncopyable.hpp>
+
+namespace caspar { namespace core { namespace diagnostics { namespace osd {
+
+void register_sink();
+void show_graphs(bool value);
+void shutdown();
+
+}}}}
--- /dev/null
+/*
+* Copyright (c) 2011 Sveriges Television AB <info@casparcg.com>
+*
+* This file is part of CasparCG (www.casparcg.com).
+*
+* CasparCG is free software: you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation, either version 3 of the License, or
+* (at your option) any later version.
+*
+* CasparCG is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with CasparCG. If not, see <http://www.gnu.org/licenses/>.
+*
+* Author: Helge Norberg, helge.norberg@svt.se
+*/
+
+#include "../StdAfx.h"
+
+#include "subject_diagnostics.h"
+
+#include "call_context.h"
+
+#include <common/diagnostics/graph.h>
+
+#include <boost/lexical_cast.hpp>
+#include <boost/timer.hpp>
+
+#include <atomic>
+#include <mutex>
+#include <unordered_map>
+
+namespace {
+
+int64_t create_id()
+{
+ static std::atomic<int64_t> counter = 0;
+
+ return ++counter;
+}
+
+}
+
+namespace caspar { namespace core { namespace diagnostics {
+
+static const double SECONDS_BETWEEN_FULL_STATE = 0.4;
+
+class subject_graph : public caspar::diagnostics::spi::graph_sink
+{
+ spl::shared_ptr<monitor::subject> subject_ = spl::make_shared<monitor::subject>("/" + boost::lexical_cast<std::string>(create_id()));
+ call_context context_ = call_context::for_thread();
+ std::mutex mutex_;
+ std::wstring text_;
+ std::unordered_map<std::string, int> colors_;
+ boost::timer time_since_full_state_send_;
+public:
+ subject_graph()
+ {
+ }
+
+ void activate() override
+ {
+ subject_->attach_parent(get_or_create_subject());
+ }
+
+ void set_text(const std::wstring& value) override
+ {
+ std::lock_guard<std::mutex> lock(mutex_);
+
+ text_ = value;
+
+ *subject_ << monitor::message("/text") % text_;
+ }
+
+ void set_value(const std::string& name, double value) override
+ {
+ *subject_ << monitor::message("/value/" + name) % value;
+
+ send_full_state_if_long_ago();
+ }
+
+ void set_color(const std::string& name, int color) override
+ {
+ std::lock_guard<std::mutex> lock(mutex_);
+
+ colors_[name] = color;
+
+ *subject_ << monitor::message("/color/" + name) % color;
+ }
+
+ void set_tag(const std::string& name) override
+ {
+ *subject_ << monitor::message("/tag/" + name);
+
+ send_full_state_if_long_ago();
+ }
+
+ void auto_reset() override
+ {
+ }
+
+ monitor::subject& subject()
+ {
+ return *subject_;
+ }
+private:
+ void send_full_state_if_long_ago()
+ {
+ std::lock_guard<std::mutex> lock(mutex_);
+
+ if (time_since_full_state_send_.elapsed() > SECONDS_BETWEEN_FULL_STATE)
+ {
+ send_full_state();
+ }
+ }
+
+ void send_full_state()
+ {
+ *subject_ << monitor::message("/text") % text_;
+
+ if (context_.video_channel != -1)
+ *subject_ << monitor::message("/context/channel") % context_.video_channel;
+
+ if (context_.layer != -1)
+ *subject_ << monitor::message("/context/layer") % context_.layer;
+
+ for (const auto& color : colors_)
+ *subject_ << monitor::message("/color/" + color.first) % color.second;
+
+ time_since_full_state_send_.restart();
+ }
+};
+
+spl::shared_ptr<monitor::subject> get_or_create_subject()
+{
+ static auto diag_subject = []()
+ {
+ auto subject = spl::make_shared<monitor::subject>("/diag");
+
+ caspar::diagnostics::spi::register_sink_factory([=]()
+ {
+ return spl::make_shared<subject_graph>();
+ });
+
+ return subject;
+ }();
+
+ return diag_subject;
+}
+
+}}}
--- /dev/null
+/*
+* Copyright (c) 2011 Sveriges Television AB <info@casparcg.com>
+*
+* This file is part of CasparCG (www.casparcg.com).
+*
+* CasparCG is free software: you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation, either version 3 of the License, or
+* (at your option) any later version.
+*
+* CasparCG is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with CasparCG. If not, see <http://www.gnu.org/licenses/>.
+*
+* Author: Helge Norberg, helge.norberg@svt.se
+*/
+
+#pragma once
+
+#include "../monitor/monitor.h"
+
+namespace caspar { namespace core { namespace diagnostics {
+
+spl::shared_ptr<monitor::subject> get_or_create_subject();
+
+}}}
#include <common/executor.h>
#include <core/mixer/image/image_mixer.h>
+#include <core/diagnostics/call_context.h>
#include <tbb/spin_mutex.h>
struct video_channel::impl final
{
- spl::shared_ptr<monitor::subject> monitor_subject_;
+ spl::shared_ptr<monitor::subject> monitor_subject_;
- const int index_;
+ const int index_;
- mutable tbb::spin_mutex format_desc_mutex_;
- core::video_format_desc format_desc_;
+ mutable tbb::spin_mutex format_desc_mutex_;
+ core::video_format_desc format_desc_;
- const spl::shared_ptr<diagnostics::graph> graph_;
-
- caspar::core::output output_;
- spl::shared_ptr<image_mixer> image_mixer_;
- caspar::core::mixer mixer_;
- caspar::core::stage stage_;
-
- executor executor_ = L"video_channel";
+ const spl::shared_ptr<caspar::diagnostics::graph> graph_ = [](int index)
+ {
+ core::diagnostics::scoped_call_context save;
+ core::diagnostics::call_context::for_thread().video_channel = index;
+ return spl::make_shared<caspar::diagnostics::graph>();
+ }(index_);
+
+ caspar::core::output output_;
+ spl::shared_ptr<image_mixer> image_mixer_;
+ caspar::core::mixer mixer_;
+ caspar::core::stage stage_;
+
+ executor executor_ = L"video_channel";
public:
impl(int index, const core::video_format_desc& format_desc, std::unique_ptr<image_mixer> image_mixer)
: monitor_subject_(spl::make_shared<monitor::subject>(
, mixer_(graph_, image_mixer_)
, stage_(graph_)
{
- graph_->set_color("tick-time", diagnostics::color(0.0f, 0.6f, 0.9f));
+ graph_->set_color("tick-time", caspar::diagnostics::color(0.0f, 0.6f, 0.9f));
graph_->set_text(print());
- diagnostics::register_graph(graph_);
+ caspar::diagnostics::register_graph(graph_);
output_.monitor_output().attach_parent(monitor_subject_);
stage_.monitor_output().attach_parent(monitor_subject_);
output_(std::move(mixed_frame), format_desc);
- graph_->set_value("tick-time", frame_timer.elapsed()*format_desc.fps*0.5);
+ auto frame_time = frame_timer.elapsed()*format_desc.fps*0.5;
+ graph_->set_value("tick-time", frame_time);
*monitor_subject_ << monitor::message("/profiler/time") % frame_timer.elapsed() % (1.0/format_desc_.fps)
<< monitor::message("/format") % format_desc.name;
return L"video_channel[" + boost::lexical_cast<std::wstring>(index_) + L"|" + video_format_desc().name + L"]";
}
+ int index() const
+ {
+ return index_;
+ }
+
boost::property_tree::wptree info() const
{
boost::property_tree::wptree info;
spl::shared_ptr<frame_factory> video_channel::frame_factory() { return impl_->image_mixer_;}
core::video_format_desc video_channel::video_format_desc() const{return impl_->video_format_desc();}
void core::video_channel::video_format_desc(const core::video_format_desc& format_desc){impl_->video_format_desc(format_desc);}
-boost::property_tree::wptree video_channel::info() const{return impl_->info();}
-monitor::subject& video_channel::monitor_output(){return *impl_->monitor_subject_;}
+boost::property_tree::wptree video_channel::info() const{return impl_->info();}
+int video_channel::index() const { return impl_->index(); }
+monitor::subject& video_channel::monitor_output(){ return *impl_->monitor_subject_; }
}}
\ No newline at end of file
// Methods
- monitor::subject& monitor_output();
+ monitor::subject& monitor_output();
// Properties
- const core::stage& stage() const;
- core::stage& stage();
- const core::mixer& mixer() const;
- core::mixer& mixer();
- const core::output& output() const;
- core::output& output();
-
- core::video_format_desc video_format_desc() const;
- void video_format_desc(const core::video_format_desc& format_desc);
-
- spl::shared_ptr<core::frame_factory> frame_factory();
+ const core::stage& stage() const;
+ core::stage& stage();
+ const core::mixer& mixer() const;
+ core::mixer& mixer();
+ const core::output& output() const;
+ core::output& output();
+
+ core::video_format_desc video_format_desc() const;
+ void video_format_desc(const core::video_format_desc& format_desc);
+
+ spl::shared_ptr<core::frame_factory> frame_factory();
- boost::property_tree::wptree info() const;
+ boost::property_tree::wptree info() const;
+ int index() const;
private:
struct impl;
spl::unique_ptr<impl> impl_;
--- /dev/null
+AUTHORS
+
+Current Contributors (sorted alphabetically):
+ - Pravin Satpute <psatpute at redhat dot com>
+ Project Owner (Current)
+ Red Hat, Inc.
+
+Previous Contributors
+
+ - Steve Matteson
+ Original Designer
+ Ascender, Inc.
--- /dev/null
+* Thu Oct 04 2012 Pravin Satpute <psatpute AT redhat DOT com>
+- Resolved "Glyphs with multiple unicode encodings inhibit subsetting" #851790
+- Resolved #851791, #854601 and #851825
+- Following GASP table version as per Liberation old version. (Anti-aliasing disabled)
+- Added support for Serbian glyphs for wikipedia #657849
+- In Monospace fonts, isFixedPitch bit set via script for getting it recognized as Monospace in putty.exe
+
+* Fri Jul 06 2012 Pravin Satpute <psatpute AT redhat DOT com>
+- Initial version of Liberation fonts based on croscore fonts version 1.21.0
+- Converted TTF files into SFD files to be open source.
+- Update Copyright and License file
+- set fsType bit to 0, Installable Embedding is allowed.
+- Absolute value in HHeadAscent/Descent values for maintaining Metric compatibility.
+
--- /dev/null
+Digitized data copyright (c) 2010 Google Corporation
+ with Reserved Font Arimo, Tinos and Cousine.
+Copyright (c) 2012 Red Hat, Inc.
+ with Reserved Font Name Liberation.
+
+This Font Software is licensed under the SIL Open Font License,
+Version 1.1.
+
+This license is copied below, and is also available with a FAQ at:
+http://scripts.sil.org/OFL
+
+SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
+
+PREAMBLE The goals of the Open Font License (OFL) are to stimulate
+worldwide development of collaborative font projects, to support the font
+creation efforts of academic and linguistic communities, and to provide
+a free and open framework in which fonts may be shared and improved in
+partnership with others.
+
+The OFL allows the licensed fonts to be used, studied, modified and
+redistributed freely as long as they are not sold by themselves.
+The fonts, including any derivative works, can be bundled, embedded,
+redistributed and/or sold with any software provided that any reserved
+names are not used by derivative works. The fonts and derivatives,
+however, cannot be released under any other type of license. The
+requirement for fonts to remain under this license does not apply to
+any document created using the fonts or their derivatives.
+
+
+
+DEFINITIONS
+"Font Software" refers to the set of files released by the Copyright
+Holder(s) under this license and clearly marked as such.
+This may include source files, build scripts and documentation.
+
+"Reserved Font Name" refers to any names specified as such after the
+copyright statement(s).
+
+"Original Version" refers to the collection of Font Software components
+as distributed by the Copyright Holder(s).
+
+"Modified Version" refers to any derivative made by adding to, deleting,
+or substituting ? in part or in whole ?
+any of the components of the Original Version, by changing formats or
+by porting the Font Software to a new environment.
+
+"Author" refers to any designer, engineer, programmer, technical writer
+or other person who contributed to the Font Software.
+
+
+PERMISSION & CONDITIONS
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of the Font Software, to use, study, copy, merge, embed, modify,
+redistribute, and sell modified and unmodified copies of the Font
+Software, subject to the following conditions:
+
+1) Neither the Font Software nor any of its individual components,in
+ Original or Modified Versions, may be sold by itself.
+
+2) Original or Modified Versions of the Font Software may be bundled,
+ redistributed and/or sold with any software, provided that each copy
+ contains the above copyright notice and this license. These can be
+ included either as stand-alone text files, human-readable headers or
+ in the appropriate machine-readable metadata fields within text or
+ binary files as long as those fields can be easily viewed by the user.
+
+3) No Modified Version of the Font Software may use the Reserved Font
+ Name(s) unless explicit written permission is granted by the
+ corresponding Copyright Holder. This restriction only applies to the
+ primary font name as presented to the users.
+
+4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
+ Software shall not be used to promote, endorse or advertise any
+ Modified Version, except to acknowledge the contribution(s) of the
+ Copyright Holder(s) and the Author(s) or with their explicit written
+ permission.
+
+5) The Font Software, modified or unmodified, in part or in whole, must
+ be distributed entirely under this license, and must not be distributed
+ under any other license. The requirement for fonts to remain under
+ this license does not apply to any document created using the Font
+ Software.
+
+
+
+TERMINATION
+This license becomes null and void if any of the above conditions are not met.
+
+
+
+DISCLAIMER
+THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
+OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
+COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
+DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM OTHER
+DEALINGS IN THE FONT SOFTWARE.
+
--- /dev/null
+ 1. What's this?
+ =================
+
+ The Liberation Fonts is font collection which aims to provide document
+ layout compatibility as usage of Times New Roman, Arial, Courier New.
+
+
+ 2. Requirements
+ =================
+
+ * fontforge is installed.
+ (http://fontforge.sourceforge.net)
+
+
+ 3. Install
+ ============
+
+ 3.1 Decompress tarball
+
+ You can extract the files by following command:
+
+ $ tar zxvf liberation-fonts-[VERSION].tar.gz
+
+ 3.2 Build from the source
+
+ Change into directory liberation-fonts-[VERSION]/ and build from sources by
+ following commands:
+
+ $ cd liberation-fonts-[VERSION]
+ $ make
+
+ The built font files will be available in 'build' directory.
+
+ 3.3 Install to system
+
+ For Fedora, you could manually install the fonts by copying the TTFs to
+ ~/.fonts for user wide usage, or to /usr/share/fonts/truetype/liberation
+ for system-wide availability. Then, run "fc-cache" to let that cached.
+
+ For other distributions, please check out corresponding documentation.
+
+
+ 4. Usage
+ ==========
+
+ Simply select preferred liberation font in applications and start using.
+
+
+ 5. License
+ ============
+
+ This Font Software is licensed under the SIL Open Font License,
+ Version 1.1.
+
+ Please read file "LICENSE" for details.
+
+
+ 6. For Maintainers
+ ====================
+
+ Before packaging a new release based on a new source tarball, you have to
+ update the version suffix in the Makefile:
+
+ VER = [VERSION]
+
+ Make sure that the defined version corresponds to the font software metadata
+ which you can check with ftinfo/otfinfo or fontforge itself. It is highly
+ recommended that file 'ChangeLog' is updated to reflect changes.
+
+ Create a tarball with the following command:
+
+ $ make dist
+
+ The new versioned tarball will be available in the dist/ folder as
+ 'liberation-fonts-[NEW_VERSION].tar.gz'.
+
+ 7. Credits
+ ============
+
+ Please read file "AUTHORS" for list of contributors.
--- /dev/null
+Here are todo for next release
+1) Serbian glyph for wikipedia https://bugzilla.redhat.com/show_bug.cgi?id=657849
+ - Improving shape of S_BE https://bugzilla.redhat.com/show_bug.cgi?id=657849#c96
+2) Liberation Mono not recognizing as Mono in Windows application #861003
+ - presently it is patch, we have to update zero width characters to fixed width
#include <common/cache_aligned_vector.h>
#include <core/consumer/frame_consumer.h>
+#include <core/diagnostics/call_context.h>
#include <tbb/concurrent_queue.h>
: config_(config)
, executor_(L"decklink_consumer[" + boost::lexical_cast<std::wstring>(config.device_index) + L"]")
{
+ auto ctx = core::diagnostics::call_context::for_thread();
executor_.begin_invoke([=]
{
+ core::diagnostics::call_context::for_thread() = ctx;
::CoInitialize(nullptr);
});
}
#include <common/env.h>
#include <core/mixer/mixer.h>
+#include <core/diagnostics/call_context.h>
#include <boost/filesystem.hpp>
#include <boost/format.hpp>
{
if(flash_producer->name() != L"flash")
{
- flash_producer = flash::create_producer(video_channel->frame_factory(), video_channel->video_format_desc(), { });
+ core::diagnostics::scoped_call_context save;
+ core::diagnostics::call_context::for_thread().video_channel = video_channel->index();
+ core::diagnostics::call_context::for_thread().layer = render_layer;
+
+ flash_producer = flash::create_producer(video_channel->frame_factory(), video_channel->video_format_desc(), {});
video_channel->stage().load(render_layer, flash_producer);
video_channel->stage().play(render_layer);
}
#include <common/log.h>
#include <common/param.h>
-#include <common/diagnostics/graph.h>
#include <common/os/windows/current_version.h>
#include <common/os/windows/system_info.h>
#include <common/base64.h>
#include <core/mixer/mixer.h>
#include <core/consumer/output.h>
#include <core/thumbnail_generator.h>
+#include <core/diagnostics/call_context.h>
+#include <core/diagnostics/osd_graph.h>
#include <modules/reroute/producer/reroute_producer.h>
#include <modules/bluefish/bluefish.h>
{
try
{
- diagnostics::show_graphs(true);
+ core::diagnostics::osd::show_graphs(true);
SetReplyString(TEXT("202 DIAG OK\r\n"));
int index = 1;
auto self = channels().back().channel;
+ core::diagnostics::scoped_call_context save;
+ core::diagnostics::call_context::for_thread().video_channel = channels().size();
+
std::vector<std::wstring> params;
params.push_back(L"SCREEN");
params.push_back(L"0");
{
if(channel.channel != self)
{
+ core::diagnostics::call_context::for_thread().layer = index;
auto producer = reroute::create_producer(*channel.channel);
self->stage().load(index, producer, false);
self->stage().play(index);
boost::to_upper(str);
}
+ core::diagnostics::scoped_call_context save;
+ core::diagnostics::call_context::for_thread().video_channel = channel_index() + 1;
+
auto consumer = create_consumer(parameters());
channel()->output().add(layer_index(consumer->index()), consumer);
//Perform loading of the clip
try
{
- auto pFP = create_producer(channel()->frame_factory(), channel()->video_format_desc(), parameters());
+ core::diagnostics::scoped_call_context save;
+ core::diagnostics::call_context::for_thread().video_channel = channel_index() + 1;
+ core::diagnostics::call_context::for_thread().layer = layer_index();
+ auto pFP = create_producer(channel()->frame_factory(), channel()->video_format_desc(), parameters());
channel()->stage().load(layer_index(), pFP, true);
SetReplyString(TEXT("202 LOAD OK\r\n"));
static boost::wregex expr(LR"(\[(?<CHANNEL>\d+)\])", boost::regex::icase);
+ core::diagnostics::scoped_call_context save;
+ core::diagnostics::call_context::for_thread().video_channel = channel_index() + 1;
+ core::diagnostics::call_context::for_thread().layer = layer_index();
+
boost::wsmatch what;
if(boost::regex_match(parameters().at(0), what, expr))
{
#include <modules/flash/producer/flash_producer.h>
#include <core/producer/transition/transition_producer.h>
#include <core/mixer/mixer.h>
+#include <core/diagnostics/call_context.h>
#include <common/env.h>
#include <boost/algorithm/string/replace.hpp>
CASPAR_LOG(error) << "Failed to save instance of " << templateName << TEXT(" as ") << titleName << TEXT(", template ") << fullTemplateFilename << " not found";
return;
}
-
+ core::diagnostics::scoped_call_context save;
+ core::diagnostics::call_context::for_thread().video_channel = 1;
+ core::diagnostics::call_context::for_thread().layer = 0;
+
auto producer = flash::create_producer(this->GetChannel()->frame_factory(), this->GetChannel()->video_format_desc(), { env::template_folder() + TEXT("CG.fth") });
std::wstringstream flashParam;
transition.type = transition_type::mix;
transition.duration = 12;
+ core::diagnostics::scoped_call_context save;
+ core::diagnostics::call_context::for_thread().video_channel = 1;
+ core::diagnostics::call_context::for_thread().layer = 0;
+
auto pFP = create_producer(GetChannel()->frame_factory(), GetChannel()->video_format_desc(), filename);
auto pTransition = create_transition_producer(GetChannel()->video_format_desc().field_mode, pFP, transition);
const std::string ipv4_address() const
{
- return socket_->is_open() ? socket_->local_endpoint().address().to_string() : "no-address";
+ return socket_->is_open() ? socket_->remote_endpoint().address().to_string() : "no-address";
}
connection(const spl::shared_ptr<tcp::socket>& socket, const protocol_strategy_factory<char>::ptr& protocol_factory, const spl::shared_ptr<connection_set>& connection_set)
#include <common/utf.h>
#include <common/memory.h>
#include <common/polling_filesystem_monitor.h>
-#include <common/diagnostics/graph.h>
#include <core/video_channel.h>
#include <core/video_format.h>
#include <core/producer/text/text_producer.h>
#include <core/consumer/output.h>
#include <core/thumbnail_generator.h>
+#include <core/diagnostics/subject_diagnostics.h>
+#include <core/diagnostics/call_context.h>
+#include <core/diagnostics/osd_graph.h>
#include <modules/bluefish/bluefish.h>
#include <modules/decklink/decklink.h>
{
protocol::asio::io_service_manager io_service_manager_;
spl::shared_ptr<monitor::subject> monitor_subject_;
+ spl::shared_ptr<monitor::subject> diag_subject_ = core::diagnostics::get_or_create_subject();
accelerator::accelerator accelerator_;
std::vector<spl::shared_ptr<IO::AsyncEventServer>> async_servers_;
std::shared_ptr<IO::AsyncEventServer> primary_amcp_server_;
: accelerator_(env::properties().get(L"configuration.accelerator", L"auto"))
, osc_client_(io_service_manager_.service())
, shutdown_server_now_(shutdown_server_now)
- {
+ {
+ core::diagnostics::osd::register_sink();
+ diag_subject_->attach_parent(monitor_subject_);
ffmpeg::init();
CASPAR_LOG(info) << L"Initialized ffmpeg module.";
image::uninit();
ffmpeg::uninit();
- diagnostics::shutdown();
+ core::diagnostics::osd::shutdown();
}
void setup_channels(const boost::property_tree::wptree& pt)
CASPAR_THROW_EXCEPTION(caspar_exception() << msg_info("Invalid video-mode."));
auto channel = spl::make_shared<video_channel>(static_cast<int>(channels_.size()+1), format_desc, accelerator_.create_image_mixer());
+
+ core::diagnostics::scoped_call_context save;
+ core::diagnostics::call_context::for_thread().video_channel = channel->index();
for (auto& xml_consumer : xml_channel.second.get_child(L"consumers"))
{
copy "$(SolutionDir)dependencies64\openal\bin\*.dll" "$(OutDir)"\r
copy "$(ProjectDir)casparcg.config" "$(OutDir)"\r
copy "$(ProjectDir)casparcg_auto_restart.bat" "$(OutDir)"\r
+copy "$(SolutionDir)dependencies64\liberation-fonts\LiberationSans-Regular.ttf" "$(OutDir)"\r
+copy "$(SolutionDir)dependencies64\liberation-fonts\LiberationSans-Regular.ttf" "$(ProjectDir)"\r
</Command>\r
</PostBuildEvent>\r
</ItemDefinitionGroup>\r
copy "$(SolutionDir)dependencies64\freeimage\bin\*.dll" "$(OutDir)"\r
copy "$(SolutionDir)dependencies64\openal\bin\*.dll" "$(OutDir)"\r
copy "$(ProjectDir)casparcg.config" "$(OutDir)"\r
-copy "$(ProjectDir)casparcg_auto_restart.bat" "$(OutDir)"</Command>\r
+copy "$(ProjectDir)casparcg_auto_restart.bat" "$(OutDir)"\r
+copy "$(SolutionDir)dependencies64\liberation-fonts\LiberationSans-Regular.ttf" "$(OutDir)"\r
+copy "$(SolutionDir)dependencies64\liberation-fonts\LiberationSans-Regular.ttf" "$(ProjectDir)"</Command>\r
</PostBuildEvent>\r
</ItemDefinitionGroup>\r
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />\r