X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=shell%2Fserver.cpp;h=40f74c0466ac1636a4eb5ff3b0a6101fd31e6b43;hb=b0a6986ce56a18a56e67be266d6d253af7cdcbb5;hp=9fbf722ea4778fba975f2a79894ec5cf87804dac;hpb=657e97e3301515e8cdfc3ca1c9d905cd477a39b9;p=casparcg diff --git a/shell/server.cpp b/shell/server.cpp index 9fbf722ea..40f74c046 100644 --- a/shell/server.cpp +++ b/shell/server.cpp @@ -1,209 +1,320 @@ -/* -* Copyright (c) 2011 Sveriges Television AB -* -* 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 . -* -* Author: Robert Nagy, ronag89@gmail.com -*/ -#include "stdafx.h" - -#include "server.h" - -#include - -#include -#include -#include - -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -namespace caspar { - -using namespace core; -using namespace protocol; - -struct server::impl : boost::noncopyable -{ - monitor::basic_subject event_subject_; - accelerator::accelerator accelerator_; - std::vector> async_servers_; - std::vector> channels_; - - impl() - : accelerator_(env::properties().get(L"configuration.accelerator", L"auto")) - { - - ffmpeg::init(); - CASPAR_LOG(info) << L"Initialized ffmpeg module."; - - bluefish::init(); - CASPAR_LOG(info) << L"Initialized bluefish module."; - - decklink::init(); - CASPAR_LOG(info) << L"Initialized decklink module."; - - oal::init(); - CASPAR_LOG(info) << L"Initialized oal module."; - - screen::init(); - CASPAR_LOG(info) << L"Initialized ogl module."; - - image::init(); - CASPAR_LOG(info) << L"Initialized image module."; - - flash::init(); - CASPAR_LOG(info) << L"Initialized flash module."; - - setup_channels(env::properties()); - CASPAR_LOG(info) << L"Initialized channels."; - - setup_controllers(env::properties()); - CASPAR_LOG(info) << L"Initialized controllers."; - } - - ~impl() - { - async_servers_.clear(); - channels_.clear(); - - Sleep(500); // HACK: Wait for asynchronous destruction of producers and consumers. - - image::uninit(); - ffmpeg::uninit(); - } - - void setup_channels(const boost::property_tree::wptree& pt) - { - using boost::property_tree::wptree; - BOOST_FOREACH(auto& xml_channel, pt.get_child(L"configuration.channels")) - { - auto format_desc = video_format_desc(xml_channel.second.get(L"video-mode", L"PAL")); - if(format_desc.format == video_format::invalid) - BOOST_THROW_EXCEPTION(caspar_exception() << msg_info("Invalid video-mode.")); - - auto channel = spl::make_shared(static_cast(channels_.size()+1), format_desc, accelerator_.create_image_mixer()); - - BOOST_FOREACH(auto& xml_consumer, xml_channel.second.get_child(L"consumers")) - { - try - { - auto name = xml_consumer.first; - if(name == L"screen") - channel->output().add(caspar::screen::create_consumer(xml_consumer.second)); - else if(name == L"bluefish") - channel->output().add(bluefish::create_consumer(xml_consumer.second)); - else if(name == L"decklink") - channel->output().add(decklink::create_consumer(xml_consumer.second)); - else if(name == L"file") - channel->output().add(ffmpeg::create_consumer(xml_consumer.second)); - else if(name == L"system-audio") - channel->output().add(oal::create_consumer()); - else if(name != L"") - CASPAR_LOG(warning) << "Invalid consumer: " << name; - } - catch(...) - { - CASPAR_LOG_CURRENT_EXCEPTION(); - } - } - - channel->subscribe(monitor::observable::observer_ptr(event_subject_)); - channels_.push_back(channel); - } - - // Dummy diagnostics channel - if(env::properties().get(L"configuration.channel-grid", false)) - channels_.push_back(spl::make_shared(static_cast(channels_.size()+1), core::video_format_desc(core::video_format::x576p2500), accelerator_.create_image_mixer())); - } - - void setup_controllers(const boost::property_tree::wptree& pt) - { - using boost::property_tree::wptree; - BOOST_FOREACH(auto& xml_controller, pt.get_child(L"configuration.controllers")) - { - try - { - auto name = xml_controller.first; - auto protocol = xml_controller.second.get(L"protocol"); - - if(name == L"tcp") - { - unsigned int port = xml_controller.second.get(L"port", 5250); - auto asyncbootstrapper = spl::make_shared(create_protocol(protocol), port); - asyncbootstrapper->Start(); - async_servers_.push_back(asyncbootstrapper); - } - else - CASPAR_LOG(warning) << "Invalid controller: " << name; - } - catch(...) - { - CASPAR_LOG_CURRENT_EXCEPTION(); - } - } - } - - spl::shared_ptr create_protocol(const std::wstring& name) const - { - if(boost::iequals(name, L"AMCP")) - return spl::make_shared(channels_); - else if(boost::iequals(name, L"CII")) - return spl::make_shared(channels_); - else if(boost::iequals(name, L"CLOCK")) - return spl::make_shared(channels_); - - BOOST_THROW_EXCEPTION(caspar_exception() << arg_name_info(L"name") << arg_value_info(name) << msg_info(L"Invalid protocol")); - } -}; - -server::server() : impl_(new impl()){} - -const std::vector> server::channels() const -{ - return impl_->channels_; -} -void server::subscribe(const monitor::observable::observer_ptr& o){impl_->event_subject_.subscribe(o);} -void server::unsubscribe(const monitor::observable::observer_ptr& o){impl_->event_subject_.unsubscribe(o);} - +/* +* Copyright (c) 2011 Sveriges Television AB +* +* 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 . +* +* Author: Robert Nagy, ronag89@gmail.com +*/ +#include "stdafx.h" + +#include "server.h" + +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +namespace caspar { + +using namespace core; +using namespace protocol; + +struct server::impl : boost::noncopyable +{ + protocol::asio::io_service_manager io_service_manager_; + spl::shared_ptr monitor_subject_; + accelerator::accelerator accelerator_; + std::vector> async_servers_; + std::shared_ptr primary_amcp_server_; + osc::client osc_client_; + std::vector> predefined_osc_subscriptions_; + std::vector> channels_; + std::shared_ptr thumbnail_generator_; + boost::promise& shutdown_server_now_; + + explicit impl(boost::promise& shutdown_server_now) + : accelerator_(env::properties().get(L"configuration.accelerator", L"auto")) + , osc_client_(io_service_manager_.service()) + , shutdown_server_now_(shutdown_server_now) + { + + ffmpeg::init(); + CASPAR_LOG(info) << L"Initialized ffmpeg module."; + + bluefish::init(); + CASPAR_LOG(info) << L"Initialized bluefish module."; + + decklink::init(); + CASPAR_LOG(info) << L"Initialized decklink module."; + + oal::init(); + CASPAR_LOG(info) << L"Initialized oal module."; + + screen::init(); + CASPAR_LOG(info) << L"Initialized ogl module."; + + image::init(); + CASPAR_LOG(info) << L"Initialized image module."; + + flash::init(); + CASPAR_LOG(info) << L"Initialized flash module."; + + psd::init(); + CASPAR_LOG(info) << L"Initialized psd module."; + + core::text::init(); + + register_producer_factory(&core::scene::create_dummy_scene_producer); + register_producer_factory(&core::scene::create_xml_scene_producer); + + setup_channels(env::properties()); + CASPAR_LOG(info) << L"Initialized channels."; + + setup_thumbnail_generation(env::properties()); + CASPAR_LOG(info) << L"Initialized thumbnail generator."; + + setup_controllers(env::properties()); + CASPAR_LOG(info) << L"Initialized controllers."; + + setup_osc(env::properties()); + CASPAR_LOG(info) << L"Initialized osc."; + } + + ~impl() + { + thumbnail_generator_.reset(); + primary_amcp_server_.reset(); + async_servers_.clear(); + channels_.clear(); + + boost::this_thread::sleep(boost::posix_time::milliseconds(500)); + //Sleep(500); // HACK: Wait for asynchronous destruction of producers and consumers. + + image::uninit(); + ffmpeg::uninit(); + } + + void setup_channels(const boost::property_tree::wptree& pt) + { + using boost::property_tree::wptree; + BOOST_FOREACH(auto& xml_channel, pt.get_child(L"configuration.channels")) + { + auto format_desc = video_format_desc(xml_channel.second.get(L"video-mode", L"PAL")); + if(format_desc.format == video_format::invalid) + CASPAR_THROW_EXCEPTION(caspar_exception() << msg_info("Invalid video-mode.")); + + auto channel = spl::make_shared(static_cast(channels_.size()+1), format_desc, accelerator_.create_image_mixer()); + + BOOST_FOREACH(auto& xml_consumer, xml_channel.second.get_child(L"consumers")) + { + try + { + auto name = xml_consumer.first; + if(name == L"screen") + channel->output().add(caspar::screen::create_consumer(xml_consumer.second, &channel->stage())); + else if(name == L"bluefish") + channel->output().add(bluefish::create_consumer(xml_consumer.second)); + else if(name == L"decklink") + channel->output().add(decklink::create_consumer(xml_consumer.second)); + else if(name == L"file") + channel->output().add(ffmpeg::create_consumer(xml_consumer.second)); + else if(name == L"system-audio") + channel->output().add(oal::create_consumer()); + else if(name != L"") + CASPAR_LOG(warning) << "Invalid consumer: " << name; + } + catch(...) + { + CASPAR_LOG_CURRENT_EXCEPTION(); + } + } + + channel->monitor_output().attach_parent(monitor_subject_); + channels_.push_back(channel); + } + + // Dummy diagnostics channel + if(env::properties().get(L"configuration.channel-grid", false)) + channels_.push_back(spl::make_shared(static_cast(channels_.size()+1), core::video_format_desc(core::video_format::x576p2500), accelerator_.create_image_mixer())); + } + + void setup_osc(const boost::property_tree::wptree& pt) + { + using boost::property_tree::wptree; + using namespace boost::asio::ip; + + monitor_subject_->attach_parent(osc_client_.sink()); + + auto default_port = + pt.get(L"configuration.osc.default-port", 6250); + auto predefined_clients = + pt.get_child_optional(L"configuration.osc.predefined-clients"); + + if (predefined_clients) + { + BOOST_FOREACH(auto& predefined_client, *predefined_clients) + { + const auto address = + predefined_client.second.get(L"address"); + const auto port = + predefined_client.second.get(L"port"); + predefined_osc_subscriptions_.push_back( + osc_client_.get_subscription_token(udp::endpoint( + address_v4::from_string(u8(address)), + port))); + } + } + + if (primary_amcp_server_) + primary_amcp_server_->add_client_lifecycle_object_factory( + [=] (const std::string& ipv4_address) + -> std::pair> + { + using namespace boost::asio::ip; + + return std::make_pair( + std::wstring(L"osc_subscribe"), + osc_client_.get_subscription_token( + udp::endpoint( + address_v4::from_string( + ipv4_address), + default_port))); + }); + } + + void setup_thumbnail_generation(const boost::property_tree::wptree& pt) + { + if (!pt.get(L"configuration.thumbnails.generate-thumbnails", true)) + return; + + auto scan_interval_millis = pt.get(L"configuration.thumbnails.scan-interval-millis", 5000); + + polling_filesystem_monitor_factory monitor_factory(scan_interval_millis); + thumbnail_generator_.reset(new thumbnail_generator( + monitor_factory, + env::media_folder(), + env::thumbnails_folder(), + pt.get(L"configuration.thumbnails.width", 256), + pt.get(L"configuration.thumbnails.height", 144), + core::video_format_desc(pt.get(L"configuration.thumbnails.video-mode", L"720p2500")), + accelerator_.create_image_mixer(), + pt.get(L"configuration.thumbnails.generate-delay-millis", 2000), + &image::write_cropped_png)); + + CASPAR_LOG(info) << L"Initialized thumbnail generator."; + } + + void setup_controllers(const boost::property_tree::wptree& pt) + { + using boost::property_tree::wptree; + BOOST_FOREACH(auto& xml_controller, pt.get_child(L"configuration.controllers")) + { + try + { + auto name = xml_controller.first; + auto protocol = xml_controller.second.get(L"protocol"); + + if(name == L"tcp") + { + unsigned int port = xml_controller.second.get(L"port", 5250); + auto asyncbootstrapper = spl::make_shared(create_protocol(protocol), port); + async_servers_.push_back(asyncbootstrapper); + + if (!primary_amcp_server_ && boost::iequals(protocol, L"AMCP")) + primary_amcp_server_ = asyncbootstrapper; + } + else + CASPAR_LOG(warning) << "Invalid controller: " << name; + } + catch(...) + { + CASPAR_LOG_CURRENT_EXCEPTION(); + } + } + } + + IO::protocol_strategy_factory::ptr create_protocol(const std::wstring& name) const + { + using namespace IO; + + if(boost::iequals(name, L"AMCP")) + return wrap_legacy_protocol("\r\n", spl::make_shared(channels_, thumbnail_generator_, shutdown_server_now_)); + else if(boost::iequals(name, L"CII")) + return wrap_legacy_protocol("\r\n", spl::make_shared(channels_)); + else if(boost::iequals(name, L"CLOCK")) + return spl::make_shared( + "ISO-8859-1", + spl::make_shared(channels_)); + + CASPAR_THROW_EXCEPTION(caspar_exception() << arg_name_info(L"name") << arg_value_info(name) << msg_info(L"Invalid protocol")); + } + +}; + +server::server(boost::promise& shutdown_server_now) : impl_(new impl(shutdown_server_now)){} + +const std::vector> server::channels() const +{ + return impl_->channels_; +} +std::shared_ptr server::get_thumbnail_generator() const {return impl_->thumbnail_generator_; } +core::monitor::subject& server::monitor_output() { return *impl_->monitor_subject_; } + } \ No newline at end of file