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 implementation(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 implementation;\r
safe_ptr<implementation> impl_;\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 "../../mixer/write_frame.h"\r
+#include "../../mixer/read_frame.h"\r
+\r
+#include <common/exception/exceptions.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(2);\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 size_t 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
+\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
+ {\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
+ 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_buffer_.size() >= 2)\r
+ { \r
+ frame_buffer_.pop();\r
+ auto frame = frame_buffer_.front();\r
+ frame_buffer_.pop();\r
+ return last_frame_ = frame;\r
+ }\r
+ else if(!frame_buffer_.empty())\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
+ core::pixel_format_desc desc;\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
+ 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
+ bool double_speed = std::abs(frame_factory_->get_video_format_desc().fps / 2.0 - format_desc.fps) < 0.01;\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
+ 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
\r
struct video_channel::implementation : boost::noncopyable\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
implementation(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 implementation;\r
safe_ptr<implementation> impl_;\r
{ \r
DEFINE_VIDEOFORMATDESC(video_format::pal ,720, 576, 1024, 576, field_mode::upper, 25, 1, boost::assign::list_of(3840), L"PAL"), \r
DEFINE_VIDEOFORMATDESC(video_format::ntsc ,720, 486, 720, 534, field_mode::lower, 30000, 1001, boost::assign::list_of(3204)(3202)(3204)(3202)(3204), L"NTSC"), \r
- DEFINE_VIDEOFORMATDESC(video_format::x576p2500 ,720, 576, 720, 576, field_mode::progressive, 25, 1, boost::assign::list_of(3840), L"576p2500"),\r
+ DEFINE_VIDEOFORMATDESC(video_format::x576p2500 ,1024, 576, 1024, 576, field_mode::progressive, 25, 1, boost::assign::list_of(3840), L"576p2500"),\r
DEFINE_VIDEOFORMATDESC(video_format::x720p2500 ,1280, 720, 1280, 720, field_mode::progressive, 25, 1, boost::assign::list_of(3840), L"720p2500"), \r
DEFINE_VIDEOFORMATDESC(video_format::x720p5000 ,1280, 720, 1280, 720, field_mode::progressive, 50, 1, boost::assign::list_of(1920), L"720p5000"), \r
DEFINE_VIDEOFORMATDESC(video_format::x720p5994 ,1280, 720, 1280, 720, field_mode::progressive, 60000, 1001, boost::assign::list_of(1602)(1601)(1602)(1601)(1602), L"720p5994"),\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 <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
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 != GetChannel())\r
+ if(channel != self)\r
{\r
- auto producer = create_channel_producer(GetChannel()->mixer(), channel); \r
- GetChannel()->stage()->load(index, producer, false);\r
- GetChannel()->stage()->play(index);\r
+ auto producer = create_channel_producer(self->mixer(), channel); \r
+ self->stage()->load(index, producer, false);\r
+ self->stage()->play(index);\r
index++;\r
}\r
}\r
transform.clip_scale[1] = delta; \r
return transform;\r
};\r
- GetChannel()->stage()->apply_frame_transform(index, transform);\r
+ self->stage()->apply_frame_transform(index, transform);\r
}\r
}\r
\r
\r
namespace amcp {\r
\r
-class ChannelGridCommand : public AMCPCommandBase<true, AddToQueue, 0>\r
+class ChannelGridCommand : public AMCPCommandBase<false, AddToQueue, 0>\r
{\r
std::wstring print() const { return L"ChannelGridCommand";}\r
bool DoExecute();\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
<?xml version="1.0" encoding="utf-8"?>\r
<configuration>\r
<paths>\r
- <media-path>C:\casparmedia\_media\</media-path>\r
+ <media-path>D:\casparcg\_media\</media-path>\r
<log-path>D:\casparcg\_log\</log-path>\r
<data-path>D:\casparcg\_data\</data-path>\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>PAL</video-mode>\r
+ <consumers>\r
+ <screen> \r
+ </screen> \r
+ </consumers>\r
+ </channel>\r
<channel>\r
<video-mode>720p5000</video-mode>\r
<consumers>\r
- <screen>\r
- <device>2</device>\r
+ <screen> \r
</screen>\r
</consumers>\r
</channel>\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>(channels_.size()+1, core::video_format_desc::get(core::video_format::x576p2500), ogl_));\r
}\r
\r
void setup_controllers(const boost::property_tree::wptree& pt)\r