<ClInclude Include="compiler\vs\disable_silly_warnings.h" />\r
<ClInclude Include="concurrency\com_context.h" />\r
<ClInclude Include="concurrency\executor.h" />\r
+ <ClInclude Include="concurrency\lock.h" />\r
<ClInclude Include="concurrency\target.h" />\r
<ClInclude Include="diagnostics\graph.h" />\r
<ClInclude Include="exception\exceptions.h" />\r
<ClInclude Include="utility\param.h">\r
<Filter>source\utility</Filter>\r
</ClInclude>\r
+ <ClInclude Include="concurrency\lock.h">\r
+ <Filter>source\concurrency</Filter>\r
+ </ClInclude>\r
</ItemGroup>\r
</Project>
\ No newline at end of file
#pragma once\r
\r
#include "../exception/win32_exception.h"\r
+#include "../exception/exceptions.h"\r
#include "../utility/string.h"\r
#include "../utility/move_on_copy.h"\r
#include "../log/log.h"\r
template<typename Func>\r
auto begin_invoke(Func&& func, task_priority priority = normal_priority) -> boost::unique_future<decltype(func())> // noexcept\r
{ \r
+ if(!is_running_)\r
+ BOOST_THROW_EXCEPTION(invalid_operation() << msg_info("executor not running."));\r
+\r
// Create a move on copy adaptor to avoid copying the functor into the queue, tbb::concurrent_queue does not support move semantics.\r
auto task_adaptor = make_move_on_copy(create_task(func));\r
\r
--- /dev/null
+#pragma once\r
+\r
+namespace caspar {\r
+\r
+template<typename T, typename F>\r
+auto lock(T& mutex, F&& func) -> decltype(func())\r
+{\r
+ T::scoped_lock lock(mutex);\r
+ return func();\r
+}\r
+\r
+}
\ No newline at end of file
#pragma warning (disable : 4244)\r
\r
#include "../concurrency/executor.h"\r
+#include "../concurrency/lock.h"\r
#include "../env.h"\r
\r
#include <SFML/Graphics.hpp>\r
#include <tbb/atomic.h>\r
#include <tbb/spin_mutex.h>\r
\r
-#include <numeric>\r
#include <array>\r
+#include <numeric>\r
+#include <tuple>\r
\r
namespace caspar { namespace diagnostics {\r
\r
{ \r
std::unique_ptr<sf::RenderWindow> window_;\r
\r
- std::list<std::shared_ptr<drawable>> drawables_;\r
+ std::list<std::weak_ptr<drawable>> drawables_;\r
\r
executor executor_;\r
public: \r
{\r
if(!window_)\r
{\r
- window_.reset(new sf::RenderWindow(sf::VideoMode(600, 1000), "CasparCG Diagnostics"));\r
+ window_.reset(new sf::RenderWindow(sf::VideoMode(750, 750), "CasparCG Diagnostics"));\r
window_->SetPosition(0, 0);\r
window_->SetActive();\r
glEnable(GL_BLEND);\r
glClear(GL_COLOR_BUFFER_BIT);\r
window_->Draw(*this);\r
window_->Display();\r
- boost::this_thread::sleep(boost::posix_time::milliseconds(20));\r
+ boost::this_thread::sleep(boost::posix_time::milliseconds(10));\r
executor_.begin_invoke([this]{tick();});\r
}\r
\r
int n = 0;\r
for(auto it = drawables_.begin(); it != drawables_.end(); ++n)\r
{\r
- auto& drawable = *it;\r
- if(!drawable.unique())\r
+ auto drawable = it->lock();\r
+ if(drawable)\r
{\r
drawable->SetScale(static_cast<float>(window_->GetWidth()), static_cast<float>(target_dy*window_->GetHeight()));\r
float target_y = std::max(last_y, static_cast<float>(n * window_->GetHeight())*target_dy);\r
\r
void do_register_drawable(const std::shared_ptr<drawable>& drawable)\r
{\r
- if(std::find(drawables_.begin(), drawables_.end(), drawable) == drawables_.end())\r
- drawables_.push_back(drawable);\r
+ drawables_.push_back(drawable);\r
+ auto it = drawables_.begin();\r
+ while(it != drawables_.end())\r
+ {\r
+ if(it->lock())\r
+ ++it;\r
+ else \r
+ it = drawables_.erase(it); \r
+ }\r
}\r
\r
static context& get_instance()\r
\r
class line : public drawable\r
{\r
- boost::circular_buffer<std::pair<double, bool>> line_data_;\r
+ boost::circular_buffer<std::pair<float, bool>> line_data_;\r
\r
- tbb::atomic<double> tick_data_;\r
+ tbb::atomic<float> tick_data_;\r
tbb::atomic<bool> tick_tag_;\r
tbb::atomic<int> color_;\r
public:\r
- line(size_t res = 600)\r
+ line(size_t res = 1200)\r
: line_data_(res)\r
{\r
- tick_data_ = 0;\r
+ tick_data_ = -1.0f;\r
color_ = 0xFFFFFFFF;\r
tick_tag_ = false;\r
\r
line_data_.push_back(std::make_pair(-1.0f, false));\r
}\r
\r
- void set_value(double value)\r
+ void set_value(float value)\r
{\r
tick_data_ = value;\r
}\r
\r
tbb::spin_mutex mutex_;\r
std::wstring text_;\r
- \r
+\r
impl()\r
{\r
}\r
-\r
+ \r
void set_text(const std::wstring& value)\r
{\r
- tbb::spin_mutex::scoped_lock lock(mutex_);\r
- text_ = value;\r
+ auto temp = value;\r
+ lock(mutex_, [&]\r
+ {\r
+ text_ = std::move(temp);\r
+ });\r
}\r
\r
void set_value(const std::string& name, double value)\r
\r
glEnable(GL_LINE_STIPPLE);\r
glLineStipple(3, 0xAAAA);\r
- glColor4f(0.8f, 0.8f, 0.8f, 1.0f); \r
+ glColor4f(1.0f, 1.0f, 1.9f, 0.5f); \r
glBegin(GL_LINE_STRIP); \r
glVertex3f(0.0f, (1.0f-0.5f) * 0.8f + 0.1f, 0.0f); \r
glVertex3f(1.0f, (1.0f-0.5f) * 0.8f + 0.1f, 0.0f); \r
if(boost::log::extract<boost::log::attributes::current_thread_id::held_type>(L"ThreadID", rec.attribute_values(), lambda::var(thread_id) = lambda::_1))\r
strm << L"[" << thread_id << L"] ";\r
\r
+\r
severity_level severity;\r
if(boost::log::extract<severity_level>(boost::log::sources::aux::severity_attribute_name<wchar_t>::get(), rec.attribute_values(), lambda::var(severity) = lambda::_1))\r
+ {\r
+ std::stringstream ss;\r
+ ss << severity;\r
strm << L"[" << severity << L"] ";\r
+ for(int n = 0; n < 7-static_cast<int>(ss.str().size()); ++n)\r
+ strm << L" ";\r
+ }\r
\r
strm << rec.message();\r
}\r
, format_desc_(format_desc)\r
, executor_(L"output")\r
{\r
- graph_->set_color("consume-time", diagnostics::color(1.0f, 0.4f, 0.0f));\r
+ graph_->set_color("consume-time", diagnostics::color(1.0f, 0.4f, 0.0f, 0.8));\r
} \r
\r
void add(int index, safe_ptr<frame_consumer> consumer)\r
return info;\r
}, high_priority));\r
}\r
+\r
+ bool empty()\r
+ {\r
+ return executor_.invoke([this]\r
+ {\r
+ return consumers_.empty();\r
+ });\r
+ }\r
};\r
\r
output::output(const safe_ptr<diagnostics::graph>& graph, const video_format_desc& format_desc, int channel_index) : impl_(new impl(graph, format_desc, channel_index)){}\r
void output::send(const std::pair<safe_ptr<read_frame>, std::shared_ptr<void>>& frame) {impl_->send(frame); }\r
void output::set_video_format_desc(const video_format_desc& format_desc){impl_->set_video_format_desc(format_desc);}\r
boost::unique_future<boost::property_tree::wptree> output::info() const{return impl_->info();}\r
+bool output::empty() const{return impl_->empty();}\r
}}
\ No newline at end of file
void set_video_format_desc(const video_format_desc& format_desc);\r
\r
boost::unique_future<boost::property_tree::wptree> info() const;\r
+\r
+ bool empty() const;\r
private:\r
struct impl;\r
safe_ptr<impl> impl_;\r
<_ProjectFileVersion>10.0.30319.1</_ProjectFileVersion>\r
<IntDir Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">$(ProjectDir)tmp\$(Configuration)\</IntDir>\r
<IntDir Condition="'$(Configuration)|$(Platform)'=='Release|x64'">$(ProjectDir)tmp\$(Configuration)\</IntDir>\r
- <IncludePath Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">..\;..\dependencies64\boost\;..\dependencies64\tbb\include\;..\dependencies64\sfml\include\;..\dependencies64\glew\include\;$(IncludePath)</IncludePath>\r
- <IncludePath Condition="'$(Configuration)|$(Platform)'=='Release|x64'">..\;..\dependencies64\boost\;..\dependencies64\tbb\include\;..\dependencies64\sfml\include\;..\dependencies64\glew\include\;$(IncludePath)</IncludePath>\r
+ <IncludePath Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">..\;..\dependencies64\boost\;..\dependencies64\tbb\include\;..\dependencies64\sfml\include\;..\dependencies64\glew\include\;..\dependencies64\asmlib\;$(IncludePath)</IncludePath>\r
+ <IncludePath Condition="'$(Configuration)|$(Platform)'=='Release|x64'">..\;..\dependencies64\boost\;..\dependencies64\tbb\include\;..\dependencies64\sfml\include\;..\dependencies64\glew\include\;..\dependencies64\asmlib\;$(IncludePath)</IncludePath>\r
<OutDir Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">$(ProjectDir)bin\$(Configuration)\</OutDir>\r
<OutDir Condition="'$(Configuration)|$(Platform)'=='Release|x64'">$(ProjectDir)bin\$(Configuration)\</OutDir>\r
<TargetName Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">$(ProjectName)</TargetName>\r
<ClInclude Include="mixer\image\blend_modes.h" />\r
<ClInclude Include="mixer\image\shader\blending_glsl.h" />\r
<ClInclude Include="mixer\image\shader\image_shader.h" />\r
+ <ClInclude Include="producer\channel\channel_producer.h" />\r
<ClInclude Include="producer\playlist\playlist_producer.h" />\r
<ClInclude Include="video_channel.h" />\r
<ClInclude Include="consumer\output.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\channel\channel_producer.cpp">\r
+ <PrecompiledHeaderFile Condition="'$(Configuration)|$(Platform)'=='Profile|Win32'">../../StdAfx.h</PrecompiledHeaderFile>\r
+ <PrecompiledHeaderFile Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">../../StdAfx.h</PrecompiledHeaderFile>\r
+ <PrecompiledHeaderFile Condition="'$(Configuration)|$(Platform)'=='Develop|Win32'">../../StdAfx.h</PrecompiledHeaderFile>\r
+ <PrecompiledHeaderFile Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">../../StdAfx.h</PrecompiledHeaderFile>\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\playlist\playlist_producer.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\mixer\image\shader">\r
<UniqueIdentifier>{e0a140f8-e217-465c-a934-163b7ea786be}</UniqueIdentifier>\r
</Filter>\r
- <Filter Include="source\producer\reroute">\r
+ <Filter Include="source\producer\channel">\r
<UniqueIdentifier>{e5def302-a2b5-4e1f-8565-d6ef3927b31f}</UniqueIdentifier>\r
</Filter>\r
</ItemGroup>\r
<ClInclude Include="mixer\image\shader\blending_glsl.h">\r
<Filter>source\mixer\image\shader</Filter>\r
</ClInclude>\r
+ <ClInclude Include="producer\channel\channel_producer.h">\r
+ <Filter>source\producer\channel</Filter>\r
+ </ClInclude>\r
</ItemGroup>\r
<ItemGroup>\r
<ClCompile Include="producer\transition\transition_producer.cpp">\r
<ClCompile Include="mixer\image\blend_modes.cpp">\r
<Filter>source\mixer\image</Filter>\r
</ClCompile>\r
+ <ClCompile Include="producer\channel\channel_producer.cpp">\r
+ <Filter>source\producer\channel</Filter>\r
+ </ClCompile>\r
</ItemGroup>\r
</Project>
\ No newline at end of file
\r
CASPAR_LOG(info) << L"Successfully initialized GLEW.";\r
\r
- GL(glGenFramebuffers(1, &fbo_)); \r
- GL(glBindFramebuffer(GL_FRAMEBUFFER_EXT, fbo_));\r
- //GL(glDisable(GL_MULTISAMPLE_ARB));\r
+ glGenFramebuffers(1, &fbo_); \r
+\r
+ CASPAR_LOG(debug) << "Created framebuffer.";\r
+\r
+ glBindFramebuffer(GL_FRAMEBUFFER, fbo_);\r
\r
CASPAR_LOG(info) << L"Successfully initialized OpenGL Device.";\r
});\r
pool.clear();\r
BOOST_FOREACH(auto& pool, host_pools_)\r
pool.clear();\r
- glDeleteFramebuffersEXT(1, &fbo_);\r
+ glDeleteFramebuffers(1, &fbo_);\r
});\r
}\r
\r
\r
void ogl_device::read_buffer(device_buffer&)\r
{\r
- if(read_buffer_ != GL_COLOR_ATTACHMENT0_EXT)\r
+ if(read_buffer_ != GL_COLOR_ATTACHMENT0)\r
{\r
- GL(glReadBuffer(GL_COLOR_ATTACHMENT0_EXT));\r
- read_buffer_ = GL_COLOR_ATTACHMENT0_EXT;\r
+ GL(glReadBuffer(GL_COLOR_ATTACHMENT0));\r
+ read_buffer_ = GL_COLOR_ATTACHMENT0;\r
}\r
}\r
\r
#include <common/concurrency/executor.h>\r
#include <common/memory/safe_ptr.h>\r
\r
+#include <gl/glew.h>\r
+\r
+#include <SFML/Window/Context.hpp>\r
+\r
#include <tbb/concurrent_unordered_map.h>\r
#include <tbb/concurrent_queue.h>\r
\r
#include <array>\r
#include <unordered_map>\r
\r
-#include <gl/glew.h>\r
-\r
-#include "../../dependencies\SFML-1.6\include\SFML/Window/Context.hpp"\r
-\r
namespace caspar { namespace core {\r
\r
class shader;\r
, audio_mixer_(graph_)\r
, executor_(L"mixer")\r
{ \r
- graph_->set_color("mix-time", diagnostics::color(1.0f, 0.0f, 0.9f));\r
+ graph_->set_color("mix-time", diagnostics::color(1.0f, 0.0f, 0.9f, 0.8));\r
}\r
\r
void send(const std::pair<std::map<int, safe_ptr<core::basic_frame>>, std::shared_ptr<void>>& packet)\r
--- /dev/null
+/*\r
+* Copyright (c) 2011 Sveriges Television AB <info@casparcg.com>\r
+*\r
+* This file is part of CasparCG (www.casparcg.com).\r
+*\r
+* CasparCG is free software: you can redistribute it and/or modify\r
+* it under the terms of the GNU General Public License as published by\r
+* the Free Software Foundation, either version 3 of the License, or\r
+* (at your option) any later version.\r
+*\r
+* CasparCG is distributed in the hope that it will be useful,\r
+* but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
+* GNU General Public License for more details.\r
+*\r
+* You should have received a copy of the GNU General Public License\r
+* along with CasparCG. If not, see <http://www.gnu.org/licenses/>.\r
+*\r
+* Author: Robert Nagy, ronag89@gmail.com\r
+*/\r
+\r
+#include "../../stdafx.h"\r
+\r
+#include "channel_producer.h"\r
+\r
+#include "../../consumer/frame_consumer.h"\r
+#include "../../consumer/output.h"\r
+#include "../../video_channel.h"\r
+\r
+#include "../frame/basic_frame.h"\r
+#include "../frame/frame_factory.h"\r
+#include "../frame/pixel_format.h"\r
+#include "../../mixer/write_frame.h"\r
+#include "../../mixer/read_frame.h"\r
+\r
+#include <common/exception/exceptions.h>\r
+\r
+#include <asmlib.h>\r
+\r
+#include <tbb/concurrent_queue.h>\r
+\r
+namespace caspar { namespace core {\r
+\r
+class channel_consumer : public frame_consumer\r
+{ \r
+ tbb::concurrent_bounded_queue<std::shared_ptr<read_frame>> frame_buffer_;\r
+ core::video_format_desc format_desc_;\r
+ int channel_index_;\r
+ tbb::atomic<bool> is_running_;\r
+\r
+public:\r
+ channel_consumer() \r
+ {\r
+ is_running_ = true;\r
+ frame_buffer_.set_capacity(3);\r
+ }\r
+\r
+ ~channel_consumer()\r
+ {\r
+ stop();\r
+ }\r
+\r
+ // frame_consumer\r
+\r
+ virtual bool send(const safe_ptr<read_frame>& frame) override\r
+ {\r
+ frame_buffer_.try_push(frame);\r
+ return is_running_;\r
+ }\r
+\r
+ virtual void initialize(const core::video_format_desc& format_desc, int channel_index) override\r
+ {\r
+ format_desc_ = format_desc;\r
+ channel_index_ = channel_index;\r
+ }\r
+\r
+ virtual std::wstring print() const override\r
+ {\r
+ return L"[channel-consumer|" + boost::lexical_cast<std::wstring>(channel_index_) + L"]";\r
+ }\r
+\r
+ virtual boost::property_tree::wptree info() const override\r
+ {\r
+ boost::property_tree::wptree info;\r
+ info.add(L"type", L"channel-consumer");\r
+ info.add(L"channel-index", channel_index_);\r
+ return info;\r
+ }\r
+ \r
+ virtual bool has_synchronization_clock() const override\r
+ {\r
+ return false;\r
+ }\r
+\r
+ virtual int buffer_depth() const override\r
+ {\r
+ return 1;\r
+ }\r
+\r
+ virtual int index() const override\r
+ {\r
+ return 78500 + channel_index_;\r
+ }\r
+\r
+ // channel_consumer\r
+\r
+ void stop()\r
+ {\r
+ is_running_ = false;\r
+ frame_buffer_.try_push(make_safe<read_frame>());\r
+ }\r
+ \r
+ const core::video_format_desc& get_video_format_desc()\r
+ {\r
+ return format_desc_;\r
+ }\r
+\r
+ std::shared_ptr<read_frame> receive()\r
+ {\r
+ if(!is_running_)\r
+ return make_safe<read_frame>();\r
+ std::shared_ptr<read_frame> frame;\r
+ frame_buffer_.try_pop(frame);\r
+ return frame;\r
+ }\r
+};\r
+ \r
+class channel_producer : public frame_producer\r
+{\r
+ const safe_ptr<frame_factory> frame_factory_;\r
+ const safe_ptr<channel_consumer> consumer_;\r
+\r
+ std::queue<safe_ptr<basic_frame>> frame_buffer_;\r
+ safe_ptr<basic_frame> last_frame_;\r
+ uint64_t frame_number_;\r
+\r
+public:\r
+ explicit channel_producer(const safe_ptr<frame_factory>& frame_factory, const safe_ptr<video_channel>& channel) \r
+ : frame_factory_(frame_factory)\r
+ , consumer_(make_safe<channel_consumer>())\r
+ , last_frame_(basic_frame::empty())\r
+ , frame_number_(0)\r
+ {\r
+ channel->output()->add(consumer_);\r
+ CASPAR_LOG(info) << print() << L" Initialized";\r
+ }\r
+\r
+ ~channel_producer()\r
+ {\r
+ consumer_->stop();\r
+ CASPAR_LOG(info) << print() << L" Uninitialized";\r
+ }\r
+\r
+ // frame_producer\r
+ \r
+ virtual safe_ptr<basic_frame> receive(int) override\r
+ {\r
+ auto format_desc = consumer_->get_video_format_desc();\r
+\r
+ if(frame_buffer_.size() > 1)\r
+ {\r
+ auto frame = frame_buffer_.front();\r
+ frame_buffer_.pop();\r
+ return last_frame_ = frame;\r
+ }\r
+ \r
+ auto read_frame = consumer_->receive();\r
+ if(!read_frame || read_frame->image_data().empty())\r
+ return basic_frame::late(); \r
+\r
+ frame_number_++;\r
+ \r
+ core::pixel_format_desc desc;\r
+ bool double_speed = std::abs(frame_factory_->get_video_format_desc().fps / 2.0 - format_desc.fps) < 0.01; \r
+ bool half_speed = std::abs(format_desc.fps / 2.0 - frame_factory_->get_video_format_desc().fps) < 0.01;\r
+\r
+ if(half_speed && frame_number_ % 2 == 0) // Skip frame\r
+ return receive(0);\r
+\r
+ desc.pix_fmt = core::pixel_format::bgra;\r
+ desc.planes.push_back(core::pixel_format_desc::plane(format_desc.width, format_desc.height, 4));\r
+ auto frame = frame_factory_->create_frame(this, desc);\r
+\r
+ A_memcpy(frame->image_data().begin(), read_frame->image_data().begin(), read_frame->image_data().size());\r
+ frame->commit();\r
+\r
+ frame_buffer_.push(frame); \r
+ \r
+ if(double_speed) \r
+ frame_buffer_.push(frame);\r
+\r
+ return receive(0);\r
+ } \r
+\r
+ virtual safe_ptr<basic_frame> last_frame() const override\r
+ {\r
+ return last_frame_; \r
+ } \r
+\r
+ virtual std::wstring print() const override\r
+ {\r
+ return L"channel[]";\r
+ }\r
+\r
+ virtual boost::property_tree::wptree info() const override\r
+ {\r
+ boost::property_tree::wptree info;\r
+ info.add(L"type", L"channel-producer");\r
+ return info;\r
+ }\r
+};\r
+\r
+safe_ptr<frame_producer> create_channel_producer(const safe_ptr<core::frame_factory>& frame_factory, const safe_ptr<video_channel>& channel)\r
+{\r
+ return make_safe<channel_producer>(frame_factory, channel);\r
+}\r
+\r
+}}
\ No newline at end of file
--- /dev/null
+/*\r
+* Copyright (c) 2011 Sveriges Television AB <info@casparcg.com>\r
+*\r
+* This file is part of CasparCG (www.casparcg.com).\r
+*\r
+* CasparCG is free software: you can redistribute it and/or modify\r
+* it under the terms of the GNU General Public License as published by\r
+* the Free Software Foundation, either version 3 of the License, or\r
+* (at your option) any later version.\r
+*\r
+* CasparCG is distributed in the hope that it will be useful,\r
+* but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
+* GNU General Public License for more details.\r
+*\r
+* You should have received a copy of the GNU General Public License\r
+* along with CasparCG. If not, see <http://www.gnu.org/licenses/>.\r
+*\r
+* Author: Robert Nagy, ronag89@gmail.com\r
+*/\r
+\r
+#pragma once\r
+\r
+#include "../frame_producer.h"\r
+\r
+#include <string>\r
+#include <vector>\r
+\r
+namespace caspar { namespace core {\r
+\r
+class video_channel;\r
+struct frame_factory;\r
+\r
+safe_ptr<frame_producer> create_channel_producer(const safe_ptr<core::frame_factory>& frame_factory, const safe_ptr<video_channel>& channel);\r
+\r
+}}\r
catch(...){}\r
\r
producer2.reset();\r
- CASPAR_LOG(debug) << str << L" Destroyed.";\r
pool->push(destroyer);\r
}); \r
}\r
}\r
}\r
\r
- virtual safe_ptr<basic_frame> receive(int flags) override {return (*producer_)->receive(flags);}\r
+ virtual safe_ptr<basic_frame> receive(int hints) override {return (*producer_)->receive(hints);}\r
virtual safe_ptr<basic_frame> last_frame() const override {return (*producer_)->last_frame();}\r
virtual std::wstring print() const override {return (*producer_)->print();}\r
virtual boost::property_tree::wptree info() const override {return (*producer_)->info();}\r
virtual uint32_t nb_frames() const override {return (*producer_)->nb_frames();}\r
};\r
\r
-safe_ptr<core::frame_producer> create_producer_destroy_proxy(safe_ptr<core::frame_producer>&& producer)\r
+safe_ptr<core::frame_producer> create_producer_destroy_proxy(safe_ptr<core::frame_producer> producer)\r
{\r
return make_safe<destroy_producer_proxy>(std::move(producer));\r
}\r
\r
+class print_producer_proxy : public frame_producer\r
+{ \r
+ std::shared_ptr<frame_producer> producer_;\r
+public:\r
+ print_producer_proxy(safe_ptr<frame_producer>&& producer) \r
+ : producer_(std::move(producer))\r
+ {\r
+ CASPAR_LOG(info) << producer_->print() << L" Initialized";\r
+ }\r
+\r
+ ~print_producer_proxy()\r
+ { \r
+ auto str = producer_->print();\r
+ producer_.reset();\r
+ CASPAR_LOG(info) << str << L" Uninitialized";\r
+ }\r
+\r
+ virtual safe_ptr<basic_frame> receive(int hints) override {return (producer_)->receive(hints);}\r
+ virtual safe_ptr<basic_frame> last_frame() const override {return (producer_)->last_frame();}\r
+ virtual std::wstring print() const override {return (producer_)->print();}\r
+ virtual boost::property_tree::wptree info() const override {return (producer_)->info();}\r
+ virtual boost::unique_future<std::wstring> call(const std::wstring& str) override {return (producer_)->call(str);}\r
+ virtual safe_ptr<frame_producer> get_following_producer() const override {return (producer_)->get_following_producer();}\r
+ virtual void set_leading_producer(const safe_ptr<frame_producer>& producer) override {(producer_)->set_leading_producer(producer);}\r
+ virtual uint32_t nb_frames() const override {return (producer_)->nb_frames();}\r
+};\r
+\r
+safe_ptr<core::frame_producer> create_producer_print_proxy(safe_ptr<core::frame_producer> producer)\r
+{\r
+ return make_safe<print_producer_proxy>(std::move(producer));\r
+}\r
+\r
+class last_frame_producer : public frame_producer\r
+{\r
+ const std::wstring print_;\r
+ const safe_ptr<basic_frame> frame_;\r
+ const uint32_t nb_frames_;\r
+public:\r
+ last_frame_producer(const safe_ptr<frame_producer>& producer) \r
+ : print_(producer->print())\r
+ , frame_(producer->last_frame() != basic_frame::eof() ? producer->last_frame() : basic_frame::empty())\r
+ , nb_frames_(producer->nb_frames())\r
+ {\r
+ }\r
+ \r
+ virtual safe_ptr<basic_frame> receive(int){return frame_;}\r
+ virtual safe_ptr<core::basic_frame> last_frame() const{return frame_;}\r
+ virtual std::wstring print() const{return L"dummy[" + print_ + L"]";}\r
+ virtual uint32_t nb_frames() const {return nb_frames_;} \r
+ virtual boost::property_tree::wptree info() const override\r
+ {\r
+ boost::property_tree::wptree info;\r
+ info.add(L"type", L"last-frame-producer");\r
+ return info;\r
+ }\r
+};\r
+\r
struct empty_frame_producer : public frame_producer\r
{\r
virtual safe_ptr<basic_frame> receive(int){return basic_frame::empty();}\r
return producer;\r
} \r
\r
-safe_ptr<basic_frame> receive_and_follow(safe_ptr<frame_producer>& producer, int flags)\r
+safe_ptr<basic_frame> receive_and_follow(safe_ptr<frame_producer>& producer, int hints)\r
{ \r
- auto frame = producer->receive(flags);\r
- if(frame != basic_frame::eof())\r
- return frame;\r
- \r
- CASPAR_LOG(info) << producer->print() << " End Of File.";\r
- auto following = producer->get_following_producer();\r
- following->set_leading_producer(producer);\r
- producer = std::move(following);\r
-\r
- return receive_and_follow(producer, flags); \r
+ auto frame = producer->receive(hints);\r
+ if(frame == basic_frame::eof())\r
+ {\r
+ CASPAR_LOG(info) << producer->print() << " End Of File.";\r
+ auto following = producer->get_following_producer();\r
+ if(following != frame_producer::empty())\r
+ {\r
+ following->set_leading_producer(producer);\r
+ producer = std::move(following);\r
+ }\r
+ else\r
+ producer = make_safe<last_frame_producer>(producer);\r
+\r
+ return receive_and_follow(producer, hints);\r
+ }\r
+ return frame;\r
}\r
\r
void register_producer_factory(const producer_factory_t& factory)\r
\r
if(producer == frame_producer::empty())\r
producer = create_playlist_producer(my_frame_factory, params);\r
+ \r
+ if(producer != frame_producer::empty())\r
+ producer = create_producer_print_proxy(producer);\r
\r
return producer;\r
}\r
std::wstring str;\r
BOOST_FOREACH(auto& param, params)\r
str += param + L" ";\r
- BOOST_THROW_EXCEPTION(file_not_found() << msg_info("No match found for supplied commands. Check syntax.") << warg_value_info(str));\r
+ BOOST_THROW_EXCEPTION(file_not_found() << msg_info("No match found for supplied commands. Check syntax.") << arg_value_info(u8(str)));\r
}\r
\r
return producer;\r
void register_producer_factory(const producer_factory_t& factory); // Not thread-safe.\r
safe_ptr<core::frame_producer> create_producer(const safe_ptr<frame_factory>&, const std::vector<std::wstring>& params);\r
safe_ptr<core::frame_producer> create_producer(const safe_ptr<frame_factory>&, const std::wstring& params);\r
-safe_ptr<core::frame_producer> create_producer_destroy_proxy(safe_ptr<core::frame_producer>&& producer);\r
+safe_ptr<core::frame_producer> create_producer_destroy_proxy(safe_ptr<core::frame_producer> producer);\r
\r
}}\r
\r
struct video_channel::impl sealed : public frame_factory\r
{\r
- const int index_;\r
- video_format_desc format_desc_;\r
- const safe_ptr<ogl_device> ogl_;\r
- safe_ptr<diagnostics::graph> graph_;\r
-\r
- safe_ptr<caspar::core::output> output_;\r
- safe_ptr<caspar::core::mixer> mixer_;\r
- safe_ptr<caspar::core::stage> stage_;\r
+ const int index_;\r
+ video_format_desc format_desc_;\r
+ const safe_ptr<ogl_device> ogl_;\r
+ const safe_ptr<diagnostics::graph> graph_;\r
+\r
+ const safe_ptr<caspar::core::output> output_;\r
+ const safe_ptr<caspar::core::mixer> mixer_;\r
+ const safe_ptr<caspar::core::stage> stage_;\r
\r
public:\r
impl(int index, const video_format_desc& format_desc, const safe_ptr<ogl_device>& ogl) \r
video_format_desc video_channel::get_video_format_desc() const{return impl_->format_desc_;}\r
void video_channel::set_video_format_desc(const video_format_desc& format_desc){impl_->set_video_format_desc(format_desc);}\r
boost::property_tree::wptree video_channel::info() const{return impl_->info();}\r
+int video_channel::index() const {return impl_->index_;}\r
\r
}}
\ No newline at end of file
\r
boost::property_tree::wptree info() const;\r
\r
+ int index() const;\r
+\r
private:\r
struct impl;\r
safe_ptr<impl> impl_;\r
BOOST_THROW_EXCEPTION(caspar_exception() \r
<< wmsg_info(print() + L" Failed to start input stream.")\r
<< boost::errinfo_api_function("StartStreams"));\r
- \r
- CASPAR_LOG(info) << print() << L" Successfully Initialized.";\r
}\r
\r
~decklink_producer()\r
{\r
context_.reset([&]{return new decklink_producer(format_desc, device_index, frame_factory, filter_str);}); \r
}\r
-\r
- ~decklink_producer_proxy()\r
- {\r
- auto str = print();\r
- context_.reset();\r
- CASPAR_LOG(info) << str << L" Successfully Uninitialized."; \r
- }\r
-\r
+ \r
// frame_producer\r
\r
virtual safe_ptr<core::basic_frame> receive(int flags) override\r
\r
THROW_ON_ERROR2(av_set_options_string(c->priv_data, options.c_str(), "=", ":"), "[ffmpeg_consumer]");\r
}\r
+ else if(c->codec_id == CODEC_ID_QTRLE)\r
+ {\r
+ c->pix_fmt = PIX_FMT_ARGB;\r
+ }\r
else\r
{\r
THROW_ON_ERROR2(av_set_options_string(c->priv_data, options.c_str(), "=", ":"), "[ffmpeg_consumer]");\r
//}\r
strcpy(prev, line);\r
sanitize((uint8_t*)line);\r
+\r
+ auto len = strlen(line);\r
+ if(len > 0)\r
+ line[len-1] = 0;\r
\r
if(level == AV_LOG_DEBUG)\r
CASPAR_LOG(debug) << L"[ffmpeg] " << line;\r
, buffer1_(AVCODEC_MAX_AUDIO_FRAME_SIZE*2)\r
, nb_frames_(0)//context->streams[index_]->nb_frames)\r
{ \r
- file_frame_number_ = 0;\r
- CASPAR_LOG(debug) << "[audio_decoder] " << context->streams[index_]->codec->codec->long_name; \r
+ file_frame_number_ = 0; \r
}\r
\r
void push(const std::shared_ptr<AVPacket>& packet)\r
{\r
return 0;//std::max<int64_t>(nb_frames_, file_frame_number_);\r
}\r
+\r
+ std::wstring print() const\r
+ { \r
+ return L"[audio-decoder] " + u16(codec_context_->codec->long_name);\r
+ }\r
};\r
\r
audio_decoder::audio_decoder(const safe_ptr<AVFormatContext>& context, const core::video_format_desc& format_desc) : impl_(new impl(context, format_desc)){}\r
std::shared_ptr<core::audio_buffer> audio_decoder::poll(){return impl_->poll();}\r
uint32_t audio_decoder::nb_frames() const{return impl_->nb_frames();}\r
uint32_t audio_decoder::file_frame_number() const{return impl_->file_frame_number_;}\r
+std::wstring audio_decoder::print() const{return impl_->print();}\r
\r
}}
\ No newline at end of file
uint32_t nb_frames() const;\r
\r
uint32_t file_frame_number() const;\r
+\r
+ std::wstring print() const;\r
private:\r
struct impl;\r
safe_ptr<impl> impl_;\r
\r
buffer2_.resize(AVCODEC_MAX_AUDIO_FRAME_SIZE*2);\r
\r
- CASPAR_LOG(warning) << L"Resampling." <<\r
- L" sample_rate:" << input_sample_rate <<\r
- L" audio_channels:" << input_channels <<\r
- L" sample_fmt:" << input_sample_format;\r
+ char sample_fmt_string[200];\r
+ av_get_sample_fmt_string(sample_fmt_string, 200, input_sample_format);\r
+\r
+ CASPAR_LOG(warning) << L"[audio-resampler]" \r
+ << L" sample-rate: " << input_sample_rate \r
+ << L" channels: " << input_channels \r
+ << L" sample-fmt: " << u16(sample_fmt_string);\r
\r
if(resampler)\r
resampler_.reset(resampler, audio_resample_close);\r
try\r
{\r
video_decoder_.reset(new video_decoder(input_.context()));\r
+ CASPAR_LOG(info) << print() << L" " << video_decoder_->print();\r
}\r
catch(averror_stream_not_found&)\r
{\r
- CASPAR_LOG(warning) << "No video-stream found. Running without video."; \r
+ //CASPAR_LOG(warning) << print() << " No video-stream found. Running without video."; \r
}\r
catch(...)\r
{\r
CASPAR_LOG_CURRENT_EXCEPTION();\r
- CASPAR_LOG(warning) << "Failed to open video-stream. Running without video."; \r
+ CASPAR_LOG(warning) << print() << "Failed to open video-stream. Running without video."; \r
}\r
\r
try\r
{\r
audio_decoder_.reset(new audio_decoder(input_.context(), frame_factory->get_video_format_desc()));\r
+ CASPAR_LOG(info) << print() << L" " << audio_decoder_->print();\r
}\r
catch(averror_stream_not_found&)\r
{\r
- CASPAR_LOG(warning) << "No audio-stream found. Running without audio."; \r
+ //CASPAR_LOG(warning) << print() << " No audio-stream found. Running without audio."; \r
}\r
catch(...)\r
{\r
CASPAR_LOG_CURRENT_EXCEPTION();\r
- CASPAR_LOG(warning) << "Failed to open audio-stream. Running without audio."; \r
+ CASPAR_LOG(warning) << print() << " Failed to open audio-stream. Running without audio."; \r
} \r
\r
if(!video_decoder_ && !audio_decoder_)\r
#include "../../ffmpeg_error.h"\r
\r
#include <common/diagnostics/graph.h>\r
+#include <common/concurrency/executor.h>\r
#include <common/exception/exceptions.h>\r
#include <common/exception/win32_exception.h>\r
#include <common/log/log.h>\r
#pragma warning (pop)\r
#endif\r
\r
-#pragma warning (disable : 4146)\r
-\r
-namespace caspar { namespace ffmpeg {\r
-\r
static const size_t MAX_BUFFER_COUNT = 100;\r
static const size_t MIN_BUFFER_COUNT = 4;\r
static const size_t MAX_BUFFER_SIZE = 16 * 1000000;\r
- \r
+\r
+namespace caspar { namespace ffmpeg {\r
+ \r
struct input::impl : boost::noncopyable\r
{ \r
- safe_ptr<diagnostics::graph> graph_;\r
+ const safe_ptr<diagnostics::graph> graph_;\r
\r
const safe_ptr<AVFormatContext> format_context_; // Destroy this last\r
const int default_stream_index_;\r
\r
const std::wstring filename_;\r
- tbb::atomic<bool> loop_;\r
const uint32_t start_; \r
const uint32_t length_;\r
+ tbb::atomic<bool> loop_;\r
uint32_t frame_number_;\r
\r
tbb::concurrent_bounded_queue<std::shared_ptr<AVPacket>> buffer_;\r
tbb::atomic<size_t> buffer_size_;\r
- boost::condition_variable buffer_cond_;\r
- boost::mutex buffer_mutex_;\r
\r
- boost::thread thread_;\r
- tbb::atomic<bool> is_running_;\r
- tbb::atomic<bool> is_eof_;\r
-\r
- tbb::recursive_mutex mutex_;\r
-\r
- explicit impl(const safe_ptr<diagnostics::graph>& graph, const std::wstring& filename, bool loop, uint32_t start, uint32_t length) \r
+ executor executor_;\r
+ \r
+ impl(const safe_ptr<diagnostics::graph> graph, const std::wstring& filename, bool loop, uint32_t start, uint32_t length) \r
: graph_(graph)\r
, format_context_(open_input(filename)) \r
, default_stream_index_(av_find_default_stream_index(format_context_.get()))\r
, start_(start)\r
, length_(length)\r
, frame_number_(0)\r
+ , executor_(print())\r
{ \r
- is_eof_ = false;\r
loop_ = loop;\r
buffer_size_ = 0;\r
\r
if(start_ > 0) \r
- do_seek(start_);\r
+ queued_seek(start_);\r
\r
graph_->set_color("seek", diagnostics::color(1.0f, 0.5f, 0.0f)); \r
graph_->set_color("buffer-count", diagnostics::color(0.7f, 0.4f, 0.4f));\r
graph_->set_color("buffer-size", diagnostics::color(1.0f, 1.0f, 0.0f)); \r
- \r
- is_running_ = true;\r
- thread_ = boost::thread([this]{run();});\r
\r
- CASPAR_LOG(info) << print() << L" Initialized.";\r
- }\r
-\r
- ~impl()\r
- {\r
- is_running_ = false;\r
- buffer_cond_.notify_all();\r
- thread_.join();\r
+ tick();\r
}\r
- \r
+ \r
bool try_pop(std::shared_ptr<AVPacket>& packet)\r
{\r
- const bool result = buffer_.try_pop(packet);\r
-\r
+ auto result = buffer_.try_pop(packet);\r
+ \r
if(result)\r
{\r
if(packet)\r
buffer_size_ -= packet->size;\r
- buffer_cond_.notify_all();\r
+ tick();\r
}\r
\r
graph_->set_value("buffer-size", (static_cast<double>(buffer_size_)+0.001)/MAX_BUFFER_SIZE);\r
graph_->set_value("buffer-count", (static_cast<double>(buffer_.size()+0.001)/MAX_BUFFER_COUNT));\r
-\r
+ \r
return result;\r
}\r
- \r
- void run()\r
- { \r
- caspar::win32_exception::install_handler();\r
\r
- try\r
+ void seek(uint32_t target)\r
+ {\r
+ executor_.begin_invoke([=]\r
{\r
- CASPAR_LOG(info) << print() << " Thread Started.";\r
+ std::shared_ptr<AVPacket> packet;\r
+ while(buffer_.try_pop(packet) && packet)\r
+ buffer_size_ -= packet->size;\r
\r
- while(is_running_)\r
- {\r
- {\r
- boost::unique_lock<boost::mutex> lock(buffer_mutex_);\r
- while(full())\r
- buffer_cond_.timed_wait(lock, boost::posix_time::millisec(20));\r
- }\r
- read_next_packet(); \r
- }\r
+ queued_seek(target);\r
\r
- CASPAR_LOG(info) << print() << " Thread Stopped.";\r
- }\r
- catch(...)\r
- {\r
- CASPAR_LOG_CURRENT_EXCEPTION();\r
- is_running_ = false;\r
- }\r
+ tick();\r
+ }, high_priority);\r
+ }\r
+ \r
+ std::wstring print() const\r
+ {\r
+ return L"ffmpeg_input[" + filename_ + L")]";\r
+ }\r
+ \r
+ bool full() const\r
+ {\r
+ return (buffer_size_ > MAX_BUFFER_SIZE || buffer_.size() > MAX_BUFFER_COUNT) && buffer_.size() > MIN_BUFFER_COUNT;\r
}\r
- \r
- void read_next_packet()\r
- { \r
- tbb::recursive_mutex::scoped_lock lock(mutex_);\r
\r
- auto packet = create_packet();\r
- auto ret = av_read_frame(format_context_.get(), packet.get()); // packet is only valid until next call of av_read_frame. Use av_dup_packet to extend its life. \r
+ void tick()\r
+ { \r
+ if(!executor_.is_running())\r
+ return;\r
\r
- if(is_eof(ret)) \r
- {\r
- frame_number_ = 0;\r
- is_eof_ = true;\r
+ executor_.begin_invoke([this]\r
+ { \r
+ if(full())\r
+ return;\r
\r
- if(loop_)\r
+ try\r
{\r
- do_seek(start_);\r
- graph_->set_tag("seek"); \r
- CASPAR_LOG(trace) << print() << " Looping."; \r
- } \r
- }\r
- else\r
- { \r
- THROW_ON_ERROR(ret, "av_read_frame", print());\r
+ auto packet = create_packet();\r
+ \r
+ auto ret = av_read_frame(format_context_.get(), packet.get()); // packet is only valid until next call of av_read_frame. Use av_dup_packet to extend its life. \r
+ \r
+ if(is_eof(ret)) \r
+ {\r
+ frame_number_ = 0;\r
+\r
+ if(loop_)\r
+ {\r
+ queued_seek(start_);\r
+ graph_->set_tag("seek"); \r
+ CASPAR_LOG(trace) << print() << " Looping."; \r
+ } \r
+ else\r
+ executor_.stop();\r
+ }\r
+ else\r
+ { \r
+ THROW_ON_ERROR(ret, "av_read_frame", print());\r
\r
- if(packet->stream_index == default_stream_index_)\r
- ++frame_number_;\r
+ if(packet->stream_index == default_stream_index_)\r
+ ++frame_number_;\r
\r
- THROW_ON_ERROR2(av_dup_packet(packet.get()), print());\r
+ THROW_ON_ERROR2(av_dup_packet(packet.get()), print());\r
\r
- // Make sure that the packet is correctly deallocated even if size and data is modified during decoding.\r
- auto size = packet->size;\r
- auto data = packet->data;\r
+ // Make sure that the packet is correctly deallocated even if size and data is modified during decoding.\r
+ auto size = packet->size;\r
+ auto data = packet->data;\r
\r
- packet = safe_ptr<AVPacket>(packet.get(), [packet, size, data](AVPacket*)\r
- {\r
- packet->size = size;\r
- packet->data = data;\r
- });\r
-\r
- buffer_.try_push(packet);\r
- buffer_size_ += packet->size;\r
+ packet = safe_ptr<AVPacket>(packet.get(), [packet, size, data](AVPacket*)\r
+ {\r
+ packet->size = size;\r
+ packet->data = data; \r
+ });\r
+\r
+ buffer_.try_push(packet);\r
+ buffer_size_ += packet->size;\r
\r
- graph_->set_value("buffer-size", (static_cast<double>(buffer_size_)+0.001)/MAX_BUFFER_SIZE);\r
- graph_->set_value("buffer-count", (static_cast<double>(buffer_.size()+0.001)/MAX_BUFFER_COUNT));\r
- } \r
- }\r
-\r
- bool full() const\r
- {\r
- return is_running_ && (is_eof_ || (buffer_size_ > MAX_BUFFER_SIZE || buffer_.size() > MAX_BUFFER_COUNT) && buffer_.size() > MIN_BUFFER_COUNT);\r
- }\r
- \r
- void do_seek(const uint32_t target)\r
+ graph_->set_value("buffer-size", (static_cast<double>(buffer_size_)+0.001)/MAX_BUFFER_SIZE);\r
+ graph_->set_value("buffer-count", (static_cast<double>(buffer_.size()+0.001)/MAX_BUFFER_COUNT));\r
+ } \r
+ \r
+ tick(); \r
+ }\r
+ catch(...)\r
+ {\r
+ CASPAR_LOG_CURRENT_EXCEPTION();\r
+ executor_.stop();\r
+ }\r
+ });\r
+ } \r
+ \r
+ void queued_seek(const uint32_t target)\r
{ \r
CASPAR_LOG(debug) << print() << " Seeking: " << target;\r
\r
auto fixed_target = (target*stream->time_base.den*codec->time_base.num)/(stream->time_base.num*codec->time_base.den)*codec->ticks_per_frame;\r
\r
THROW_ON_ERROR2(avformat_seek_file(format_context_.get(), default_stream_index_, std::numeric_limits<int64_t>::min(), fixed_target, std::numeric_limits<int64_t>::max(), 0), print()); \r
-\r
- is_eof_ = false;\r
- buffer_cond_.notify_all();\r
-\r
+ \r
auto flush_packet = create_packet();\r
flush_packet->data = nullptr;\r
flush_packet->size = 0;\r
buffer_.push(flush_packet);\r
} \r
\r
- void seek(uint32_t target)\r
- {\r
- tbb::recursive_mutex::scoped_lock lock(mutex_);\r
-\r
- std::shared_ptr<AVPacket> packet;\r
- while(try_pop(packet))\r
- {\r
- }\r
-\r
- do_seek(target);\r
- }\r
-\r
bool is_eof(int ret)\r
{\r
+ #pragma warning (disable : 4146)\r
+\r
if(ret == AVERROR(EIO))\r
CASPAR_LOG(trace) << print() << " Received EIO, assuming EOF. ";\r
if(ret == AVERROR_EOF)\r
\r
return ret == AVERROR_EOF || ret == AVERROR(EIO) || frame_number_ >= length_; // av_read_frame doesn't always correctly return AVERROR_EOF;\r
}\r
- \r
- std::wstring print() const\r
- {\r
- return L"ffmpeg_input[" + filename_ + L")]";\r
- }\r
};\r
\r
input::input(const safe_ptr<diagnostics::graph>& graph, const std::wstring& filename, bool loop, uint32_t start, uint32_t length) \r
: impl_(new impl(graph, filename, loop, start, length)){}\r
-bool input::eof() const {return impl_->is_eof_;}\r
+bool input::eof() const {return !impl_->executor_.is_running();}\r
bool input::try_pop(std::shared_ptr<AVPacket>& packet){return impl_->try_pop(packet);}\r
safe_ptr<AVFormatContext> input::context(){return impl_->format_context_;}\r
void input::loop(bool value){impl_->loop_ = value;}\r
{\r
video_streams_.push(std::queue<safe_ptr<write_frame>>());\r
audio_streams_.push(core::audio_buffer());\r
- boost::range::sort(audio_cadence_);\r
- boost::range::reverse(audio_cadence_);\r
+ \r
+ // Note: Uses 1 step rotated cadence for 1001 modes (1602, 1602, 1601, 1602, 1601)\r
+ // This cadence fills the audio mixer most optimally.\r
+ boost::range::rotate(audio_cadence_, std::end(audio_cadence_)-1);\r
}\r
\r
void push(const std::shared_ptr<AVFrame>& video_frame, int flags)\r
, height_(codec_context_->height)\r
{\r
file_frame_number_ = 0;\r
- CASPAR_LOG(debug) << "[video_decoder] " << context->streams[index_]->codec->codec->long_name;\r
}\r
\r
void push(const std::shared_ptr<AVPacket>& packet)\r
\r
bool ready() const\r
{\r
- return packets_.size() > 10;\r
+ return packets_.size() >= 8;\r
}\r
\r
uint32_t nb_frames() const\r
{\r
return std::max<uint32_t>(nb_frames_, file_frame_number_);\r
}\r
+\r
+ std::wstring print() const\r
+ { \r
+ return L"[video-decoder] " + u16(codec_context_->codec->long_name);\r
+ }\r
};\r
\r
video_decoder::video_decoder(const safe_ptr<AVFormatContext>& context) : impl_(new impl(context)){}\r
uint32_t video_decoder::nb_frames() const{return impl_->nb_frames();}\r
uint32_t video_decoder::file_frame_number() const{return impl_->file_frame_number_;}\r
bool video_decoder::is_progressive() const{return impl_->is_progressive_;}\r
+std::wstring video_decoder::print() const{return impl_->print();}\r
\r
}}
\ No newline at end of file
\r
bool is_progressive() const;\r
\r
+ std::wstring print() const;\r
+\r
private:\r
struct impl;\r
safe_ptr<impl> impl_;\r
}\r
else if(str.find(TEXT("OnCommand")) != std::wstring::npos) {\r
//this is how templatehost 1.8 reports that a command has been received\r
- CASPAR_LOG(debug) << print_() << L" Command: \n-------------------------------------------\n" << str << L"\n-------------------------------------------";\r
+ CASPAR_LOG(debug) << print_() << L" [command] " << str;\r
bCallSuccessful_ = true;\r
}\r
else if(str.find(TEXT("Activity")) != std::wstring::npos)\r
{\r
- CASPAR_LOG(debug) << print_() << L" Activity: \n-------------------------------------------\n" << str << L"\n-------------------------------------------";\r
+ CASPAR_LOG(debug) << print_() << L" [activity] " << str;\r
\r
//this is how templatehost 1.7 reports that a command has been received\r
if(str.find(TEXT("Command recieved")) != std::wstring::npos)\r
}\r
else if(str.find(TEXT("OnNotify")) != std::wstring::npos)\r
{\r
- CASPAR_LOG(info) << print_() << L" Notification: \n-------------------------------------------\n" << str << L"\n-------------------------------------------";\r
+ CASPAR_LOG(info) << print_() << L" [notification] " << str;\r
\r
//if(pFlashProducer_ != 0 && pFlashProducer_->pMonitor_) {\r
// std::wstring::size_type pos = str.find(TEXT('@'));\r
}\r
else if(str.find(TEXT("IsEmpty")) != std::wstring::npos)\r
{\r
- CASPAR_LOG(info) << print_() << L" Empty.";\r
+ CASPAR_LOG(trace) << print_() << L" Empty.";\r
ATLTRACE(_T("ShockwaveFlash::IsEmpty\n"));\r
bIsEmpty_ = true;\r
}\r
else if(str.find(TEXT("OnError")) != std::wstring::npos)\r
{\r
- CASPAR_LOG(error) << print_() << L" Error: \n-------------------------------------------\n" << str << L"\n-------------------------------------------";\r
+ CASPAR_LOG(error) << print_() << L" [error] " << str;\r
}\r
else if(str.find(TEXT("OnDebug")) != std::wstring::npos)\r
{\r
- CASPAR_LOG(error) << print_() << L" Debug: \n-------------------------------------------\n" << str << L"\n-------------------------------------------";\r
+ CASPAR_LOG(debug) << print_() << L" [debug] " << str;\r
}\r
//else if(str.find(TEXT("OnTemplateDescription")) != std::wstring::npos)\r
//{\r
{\r
if(flash_producer->print().find(L"flash[") == std::string::npos) // UGLY hack\r
{\r
- flash_producer = make_safe<cg_producer>(flash::create_producer(video_channel->frame_factory(), boost::assign::list_of<std::wstring>())); \r
+ flash_producer = flash::create_producer(video_channel->frame_factory(), boost::assign::list_of<std::wstring>()); \r
video_channel->stage()->load(render_layer, flash_producer); \r
video_channel->stage()->play(render_layer);\r
}\r
throw;\r
}\r
\r
- return static_pointer_cast<cg_producer>(flash_producer);\r
+ return make_safe<cg_producer>(flash_producer);\r
}\r
\r
safe_ptr<core::frame_producer> create_ct_producer(const safe_ptr<core::frame_factory> frame_factory, const std::vector<std::wstring>& params) \r
if(FAILED(CComObject<caspar::flash::FlashAxContainer>::CreateInstance(&ax_)))\r
BOOST_THROW_EXCEPTION(caspar_exception() << wmsg_info(print() + L" Failed to create FlashAxContainer"));\r
\r
- ax_->set_print([this]{return L"flash_renderer";});\r
+ ax_->set_print([this]{return print();});\r
\r
if(FAILED(ax_->CreateAxControl()))\r
BOOST_THROW_EXCEPTION(caspar_exception() << wmsg_info(print() + L" Failed to Create FlashAxControl"));\r
\r
ax_->SetSize(width_, height_); \r
\r
- CASPAR_LOG(info) << print() << L" Successfully initialized with template-host: " << filename << L" width: " << width_ << L" height: " << height_ << L".";\r
+ CASPAR_LOG(info) << print() << L" Initialized.";\r
}\r
\r
~flash_renderer()\r
std::wstring result;\r
\r
if(!ax_->FlashCall(param, result))\r
- CASPAR_LOG(warning) << print() << L" Flash call failed:" << param;//BOOST_THROW_EXCEPTION(invalid_operation() << wmsg_info("Flash function call failed.") << arg_name_info("param") << arg_value_info(u8(param)));\r
+ CASPAR_LOG(warning) << print() << L" Flash call failed:" << param;//BOOST_THROW_EXCEPTION(invalid_operation() << msg_info("Flash function call failed.") << arg_name_info("param") << arg_value_info(narrow(param)));\r
graph_->set_tag("param");\r
\r
return result;\r
{\r
float frame_time = 1.0f/ax_->GetFPS();\r
\r
- graph_->set_value("tick-time", (tick_timer_.elapsed()/frame_time)*0.5f);\r
+ graph_->set_value("tick-time", static_cast<float>(tick_timer_.elapsed()/frame_time)*0.5f);\r
tick_timer_.restart();\r
\r
if(ax_->IsEmpty())\r
\r
std::wstring print()\r
{\r
- return L"flash[" + boost::filesystem::path(filename_).filename().wstring() + L"]"; \r
+ return L"flash-player[" + boost::filesystem::wpath(filename_).filename().wstring() \r
+ + L"|" + boost::lexical_cast<std::wstring>(width_)\r
+ + L"x" + boost::lexical_cast<std::wstring>(height_)\r
+ + L"]"; \r
}\r
};\r
\r
, height_(height > 0 ? height : frame_factory->get_video_format_desc().height)\r
, context_(L"flash_producer")\r
{ \r
- if(!boost::filesystem::exists(filename))\r
- BOOST_THROW_EXCEPTION(file_not_found() << werr_file_name_info(filename)); \r
-\r
fps_ = 0;\r
\r
graph_->set_color("output-buffer-count", diagnostics::color(1.0f, 1.0f, 0.0f)); \r
{\r
auto template_host = get_template_host(frame_factory->get_video_format_desc());\r
\r
- return create_producer_destroy_proxy(make_safe<flash_producer>(frame_factory, env::template_folder() + L"\\" + template_host.filename, template_host.width, template_host.height));\r
+ auto filename = env::template_folder() + L"\\" + template_host.filename;\r
+ \r
+ if(!boost::filesystem::exists(filename))\r
+ BOOST_THROW_EXCEPTION(file_not_found() << boost::errinfo_file_name(u8(filename))); \r
+\r
+ return create_producer_destroy_proxy(make_safe<flash_producer>(frame_factory, filename, template_host.width, template_host.height));\r
}\r
\r
std::wstring find_template(const std::wstring& template_name)\r
std::copy_n(FreeImage_GetBits(bitmap.get()), frame->image_data().size(), frame->image_data().begin());\r
frame->commit();\r
frame_ = std::move(frame);\r
-\r
- CASPAR_LOG(info) << print() << L" Initialized";\r
}\r
\r
// frame_producer\r
\r
configuration config;\r
\r
- if(params.size() > 1) \r
- config.screen_index = lexical_cast_or_default<int>(params[2], config.screen_index);\r
-\r
- if(params.size() > 2) \r
- config.windowed = lexical_cast_or_default<bool>(params[3], config.windowed);\r
-\r
+ auto device_it = std::find(params.begin(), params.end(), L"DEVICE");\r
+ if(device_it != params.end() && ++device_it != params.end())\r
+ config.screen_index = boost::lexical_cast<int>(*device_it);\r
+ \r
+ config.key_only = std::find(params.begin(), params.end(), L"WINDOWED") != params.end();\r
config.key_only = std::find(params.begin(), params.end(), L"KEY_ONLY") != params.end();\r
\r
+ auto name_it = std::find(params.begin(), params.end(), L"NAME");\r
+ if(name_it != params.end() && ++name_it != params.end())\r
+ config.name = *name_it;\r
+\r
return make_safe<ogl_consumer_proxy>(config);\r
}\r
\r
#include <core/producer/frame_producer.h>\r
#include <core/video_format.h>\r
#include <core/producer/transition/transition_producer.h>\r
+#include <core/producer/channel/channel_producer.h>\r
#include <core/producer/frame/frame_transform.h>\r
#include <core/producer/stage.h>\r
#include <core/producer/layer.h>\r
#include <modules/flash/producer/cg_producer.h>\r
#include <modules/ffmpeg/producer/util/util.h>\r
#include <modules/image/image.h>\r
+#include <modules/ogl/ogl.h>\r
\r
#include <algorithm>\r
#include <locale>\r
}\r
}\r
\r
+bool ChannelGridCommand::DoExecute()\r
+{\r
+ int index = 1;\r
+ auto self = GetChannels().back();\r
+ \r
+ std::vector<std::wstring> params;\r
+ params.push_back(L"SCREEN");\r
+ params.push_back(L"NAME");\r
+ params.push_back(L"Channel Grid Window");\r
+ auto screen = create_consumer(params);\r
+\r
+ self->output()->add(screen);\r
+\r
+ BOOST_FOREACH(auto channel, GetChannels())\r
+ {\r
+ if(channel != self)\r
+ {\r
+ auto producer = create_channel_producer(self->frame_factory(), channel); \r
+ self->stage()->load(index, producer, false);\r
+ self->stage()->play(index);\r
+ index++;\r
+ }\r
+ }\r
+\r
+ int n = GetChannels().size()-1;\r
+ double delta = 1.0/static_cast<double>(n);\r
+ for(int x = 0; x < n; ++x)\r
+ {\r
+ for(int y = 0; y < n; ++y)\r
+ {\r
+ int index = x+y*n+1;\r
+ auto transform = [=](frame_transform transform) -> frame_transform\r
+ { \r
+ transform.fill_translation[0] = x*delta;\r
+ transform.fill_translation[1] = y*delta;\r
+ transform.fill_scale[0] = delta;\r
+ transform.fill_scale[1] = delta;\r
+ transform.clip_translation[0] = x*delta;\r
+ transform.clip_translation[1] = y*delta;\r
+ transform.clip_scale[0] = delta;\r
+ transform.clip_scale[1] = delta; \r
+ return transform;\r
+ };\r
+ self->stage()->apply_frame_transform(index, transform);\r
+ }\r
+ }\r
+\r
+ return true;\r
+}\r
+\r
bool CallCommand::DoExecute()\r
{ \r
//Perform loading of the clip\r
\r
bool CGCommand::DoExecute()\r
{\r
- std::wstring command = _parameters[0];\r
- if(command == TEXT("ADD"))\r
- return DoExecuteAdd();\r
- else if(command == TEXT("PLAY"))\r
- return DoExecutePlay();\r
- else if(command == TEXT("STOP"))\r
- return DoExecuteStop();\r
- else if(command == TEXT("NEXT"))\r
- return DoExecuteNext();\r
- else if(command == TEXT("REMOVE"))\r
- return DoExecuteRemove();\r
- else if(command == TEXT("CLEAR"))\r
- return DoExecuteClear();\r
- else if(command == TEXT("UPDATE"))\r
- return DoExecuteUpdate();\r
- else if(command == TEXT("INVOKE"))\r
- return DoExecuteInvoke();\r
- else if(command == TEXT("INFO"))\r
- return DoExecuteInfo();\r
+ try\r
+ {\r
+ std::wstring command = _parameters[0];\r
+ if(command == TEXT("ADD"))\r
+ return DoExecuteAdd();\r
+ else if(command == TEXT("PLAY"))\r
+ return DoExecutePlay();\r
+ else if(command == TEXT("STOP"))\r
+ return DoExecuteStop();\r
+ else if(command == TEXT("NEXT"))\r
+ return DoExecuteNext();\r
+ else if(command == TEXT("REMOVE"))\r
+ return DoExecuteRemove();\r
+ else if(command == TEXT("CLEAR"))\r
+ return DoExecuteClear();\r
+ else if(command == TEXT("UPDATE"))\r
+ return DoExecuteUpdate();\r
+ else if(command == TEXT("INVOKE"))\r
+ return DoExecuteInvoke();\r
+ else if(command == TEXT("INFO"))\r
+ return DoExecuteInfo();\r
+ }\r
+ catch(...)\r
+ {\r
+ CASPAR_LOG_CURRENT_EXCEPTION();\r
+ }\r
\r
SetReplyString(TEXT("403 CG ERROR\r\n"));\r
return false;\r
\r
namespace amcp {\r
\r
+class ChannelGridCommand : public AMCPCommandBase<false, AddToQueue, 0>\r
+{\r
+ std::wstring print() const { return L"ChannelGridCommand";}\r
+ bool DoExecute();\r
+};\r
+\r
class DiagnosticsCommand : public AMCPCommandBase<false, AddToQueue, 0>\r
{\r
std::wstring print() const { return L"DiagnosticsCommand";}\r
AMCPCommandPtr pCommand;\r
MessageParserState state = New;\r
\r
- CASPAR_LOG(info) << L"Received: " << message;\r
-\r
std::size_t tokensInMessage = TokenizeMessage(message, &tokens);\r
\r
//parse the message one token at the time\r
}\r
else\r
{\r
+ pCommand->SetChannels(channels_);\r
//Set scheduling\r
if(commandSwitch.size() > 0) {\r
transform(commandSwitch.begin(), commandSwitch.end(), commandSwitch.begin(), toupper);\r
}\r
\r
pCommand->SetChannel(pChannel);\r
- pCommand->SetChannels(channels_);\r
pCommand->SetChannelIndex(channelIndex);\r
pCommand->SetLayerIntex(layerIndex);\r
\r
std::wstring s = str;\r
transform(s.begin(), s.end(), s.begin(), toupper);\r
\r
- if (s == TEXT("MIXER")) return std::make_shared<MixerCommand>();\r
- else if(s == TEXT("DIAG")) return std::make_shared<DiagnosticsCommand>();\r
- else if(s == TEXT("CALL")) return std::make_shared<CallCommand>();\r
- else if(s == TEXT("SWAP")) return std::make_shared<SwapCommand>();\r
- else if(s == TEXT("LOAD")) return std::make_shared<LoadCommand>();\r
- else if(s == TEXT("LOADBG")) return std::make_shared<LoadbgCommand>();\r
- else if(s == TEXT("ADD")) return std::make_shared<AddCommand>();\r
- else if(s == TEXT("REMOVE")) return std::make_shared<RemoveCommand>();\r
- else if(s == TEXT("PAUSE")) return std::make_shared<PauseCommand>();\r
- else if(s == TEXT("PLAY")) return std::make_shared<PlayCommand>();\r
- else if(s == TEXT("STOP")) return std::make_shared<StopCommand>();\r
- else if(s == TEXT("CLEAR")) return std::make_shared<ClearCommand>();\r
- else if(s == TEXT("PRINT")) return std::make_shared<PrintCommand>();\r
- else if(s == TEXT("LOG")) return std::make_shared<LogCommand>();\r
- else if(s == TEXT("CG")) return std::make_shared<CGCommand>();\r
- else if(s == TEXT("DATA")) return std::make_shared<DataCommand>();\r
- else if(s == TEXT("CINF")) return std::make_shared<CinfCommand>();\r
- else if(s == TEXT("INFO")) return std::make_shared<InfoCommand>(channels_);\r
- else if(s == TEXT("CLS")) return std::make_shared<ClsCommand>();\r
- else if(s == TEXT("TLS")) return std::make_shared<TlsCommand>();\r
- else if(s == TEXT("VERSION")) return std::make_shared<VersionCommand>();\r
- else if(s == TEXT("BYE")) return std::make_shared<ByeCommand>();\r
- else if(s == TEXT("SET")) return std::make_shared<SetCommand>();\r
+ if (s == TEXT("MIXER")) return std::make_shared<MixerCommand>();\r
+ else if(s == TEXT("DIAG")) return std::make_shared<DiagnosticsCommand>();\r
+ else if(s == TEXT("CHANNEL_GRID")) return std::make_shared<ChannelGridCommand>();\r
+ else if(s == TEXT("CALL")) return std::make_shared<CallCommand>();\r
+ else if(s == TEXT("SWAP")) return std::make_shared<SwapCommand>();\r
+ else if(s == TEXT("LOAD")) return std::make_shared<LoadCommand>();\r
+ else if(s == TEXT("LOADBG")) return std::make_shared<LoadbgCommand>();\r
+ else if(s == TEXT("ADD")) return std::make_shared<AddCommand>();\r
+ else if(s == TEXT("REMOVE")) return std::make_shared<RemoveCommand>();\r
+ else if(s == TEXT("PAUSE")) return std::make_shared<PauseCommand>();\r
+ else if(s == TEXT("PLAY")) return std::make_shared<PlayCommand>();\r
+ else if(s == TEXT("STOP")) return std::make_shared<StopCommand>();\r
+ else if(s == TEXT("CLEAR")) return std::make_shared<ClearCommand>();\r
+ else if(s == TEXT("PRINT")) return std::make_shared<PrintCommand>();\r
+ else if(s == TEXT("LOG")) return std::make_shared<LogCommand>();\r
+ else if(s == TEXT("CG")) return std::make_shared<CGCommand>();\r
+ else if(s == TEXT("DATA")) return std::make_shared<DataCommand>();\r
+ else if(s == TEXT("CINF")) return std::make_shared<CinfCommand>();\r
+ else if(s == TEXT("INFO")) return std::make_shared<InfoCommand>(channels_);\r
+ else if(s == TEXT("CLS")) return std::make_shared<ClsCommand>();\r
+ else if(s == TEXT("TLS")) return std::make_shared<TlsCommand>();\r
+ else if(s == TEXT("VERSION")) return std::make_shared<VersionCommand>();\r
+ else if(s == TEXT("BYE")) return std::make_shared<ByeCommand>();\r
+ else if(s == TEXT("SET")) return std::make_shared<SetCommand>();\r
//else if(s == TEXT("MONITOR"))\r
//{\r
// result = AMCPCommandPtr(new MonitorCommand());\r
\r
#include "../StdAfx.h"\r
\r
+#pragma warning (disable: 4244)\r
+\r
#include "CIIProtocolStrategy.h"\r
#include "CIICommandsImpl.h"\r
#include <sstream>\r
#include <algorithm>\r
#include <modules/flash/producer/cg_producer.h>\r
+#include <boost/locale.hpp>\r
\r
namespace caspar { namespace protocol { namespace cii {\r
\r
pCIIStrategy_->DisplayMediaFile(filename_); \r
\r
//TODO: Need to be checked for validity\r
- else if(state_ == 1)\r
- flash::get_default_cg_producer(pCIIStrategy_->GetChannel())->add(layer_, filename_, false, TEXT(""), xmlData_);\r
+ else if(state_ == 1) \r
+ {\r
+ // HACK fix. The data sent is UTF8, however the protocol is implemented for ISO-8859-1. Instead of doing risky changes we simply convert into proper encoding when leaving protocol code.\r
+ auto xmlData2 = boost::locale::conv::utf_to_utf<wchar_t, char>(std::string(xmlData_.begin(), xmlData_.end()));\r
+ flash::get_default_cg_producer(pCIIStrategy_->GetChannel())->add(layer_, filename_, false, TEXT(""), xmlData2);\r
+ }\r
}\r
\r
\r
\r
void CIIProtocolStrategy::ProcessMessage(const std::wstring& message)\r
{\r
- CASPAR_LOG(debug) << L"Received: " << message.c_str();\r
-\r
std::vector<std::wstring> tokens;\r
int tokenCount = TokenizeMessage(message, &tokens);\r
\r
\r
#include <common/log/log.h>\r
\r
+#include <boost/algorithm/string/replace.hpp>\r
+\r
#include <string>\r
#include <algorithm>\r
\r
\r
//Convert to widechar\r
if(ConvertMultiByteToWideChar(pProtocolStrategy_->GetCodepage(), pSI->recvBuffer_, recvResult + pSI->recvLeftoverOffset_, pSI->wideRecvBuffer_, pSI->recvLeftoverOffset_))\r
+ {\r
+ auto msg = std::wstring(pSI->wideRecvBuffer_.begin(), pSI->wideRecvBuffer_.end());\r
+ boost::replace_all(msg, L"\n", L"\\n");\r
+ boost::replace_all(msg, L"\r", L"\\r");\r
+\r
+ CASPAR_LOG(info) << L"Received message from " << pSI->host_.c_str() << ": "<< msg;\r
pProtocolStrategy_->Parse(&pSI->wideRecvBuffer_[0], pSI->wideRecvBuffer_.size(), pSI);\r
+ }\r
else \r
CASPAR_LOG(error) << "Read from " << pSI->host_.c_str() << TEXT(" failed, could not convert command to UNICODE");\r
\r
- \r
-\r
maxRecvLength = sizeof(pSI->recvBuffer_)-pSI->recvLeftoverOffset_;\r
recvResult = recv(pSI->socket_, pSI->recvBuffer_+pSI->recvLeftoverOffset_, maxRecvLength, 0);\r
}\r
}\r
else {\r
if(sentBytes == bytesToSend) {\r
+ \r
if(sentBytes < 512)\r
- CASPAR_LOG(info) << "Sent: " << socketInfo.sendQueue_.front().c_str() << TEXT(" to ") << socketInfo.host_.c_str();\r
+ {\r
+ boost::replace_all(socketInfo.sendQueue_.front(), L"\n", L"\\n");\r
+ boost::replace_all(socketInfo.sendQueue_.front(), L"\r", L"\\r");\r
+ CASPAR_LOG(info) << L"Sent message to " << socketInfo.host_.c_str() << L": " << socketInfo.sendQueue_.front().c_str();\r
+ }\r
else\r
CASPAR_LOG(info) << "Sent more than 512 bytes to " << socketInfo.host_.c_str();\r
\r
{\r
void Send(const std::wstring& data)\r
{\r
- std::wcout << (L"\n--------------------\n" + data + L"--------------------\n");\r
+ std::wcout << (L"#" + data);\r
}\r
void Disconnect(){}\r
};\r
<template-path>D:\casparcg\_templates\</template-path>\r
</paths>\r
<log-level>trace</log-level>\r
+ <channel-grid>true</channel-grid>\r
<channels>\r
<channel>\r
<video-mode>720p5000</video-mode>\r
<consumers>\r
<screen></screen>\r
+ <system-audio></system-audio>\r
</consumers>\r
</channel>\r
</channels>\r
}\r
} \r
}\r
+\r
+ // Dummy diagnostics channel\r
+ if(env::properties().get(L"configuration.channel-grid", false))\r
+ channels_.push_back(make_safe<video_channel>(static_cast<int>(channels_.size()+1), core::video_format_desc(core::video_format::x576p2500), ogl_));\r
}\r
\r
void setup_controllers(const boost::property_tree::wptree& pt)\r