X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=shell%2Fserver.cpp;h=8dde528c742ec2ce931f9016737fe14c9476d562;hb=ff4370eb10d963c970c0652a913f47c36e535809;hp=3b86e2cc97ea62b17ededfba30f4faabb0c9b511;hpb=0eba5506e33ad7298f70cae43f57384ccc6a3787;p=casparcg diff --git a/shell/server.cpp b/shell/server.cpp index 3b86e2cc9..8dde528c7 100644 --- a/shell/server.cpp +++ b/shell/server.cpp @@ -1,46 +1,59 @@ /* -* copyright (c) 2010 Sveriges Television AB +* Copyright 2013 Sveriges Television AB http://casparcg.com/ * -* This file is part of CasparCG. +* 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 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 . +* 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 "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 @@ -48,6 +61,9 @@ #include #include #include +#include +#include +#include #include #include @@ -62,13 +78,24 @@ using namespace protocol; struct server::implementation : boost::noncopyable { + protocol::asio::io_service_manager io_service_manager_; + safe_ptr monitor_subject_; + boost::promise& shutdown_server_now_; safe_ptr ogl_; 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_; + + implementation(boost::promise& shutdown_server_now) + : shutdown_server_now_(shutdown_server_now) + , ogl_(ogl_device::create()) + , osc_client_(io_service_manager_.service()) + { + setup_audio(env::properties()); - implementation() - : ogl_(ogl_device::create()) - { ffmpeg::init(); CASPAR_LOG(info) << L"Initialized ffmpeg module."; @@ -77,88 +104,163 @@ struct server::implementation : boost::noncopyable decklink::init(); CASPAR_LOG(info) << L"Initialized decklink module."; + + portaudio::init(); + CASPAR_LOG(info) << L"Initialized portaudio module."; - flash::init(); - CASPAR_LOG(info) << L"Initialized flash module."; - - oal::init(); - CASPAR_LOG(info) << L"Initialized oal module."; - + newtek::init(); + CASPAR_LOG(info) << L"Initialized newtek module."; + ogl::init(); CASPAR_LOG(info) << L"Initialized ogl module."; + flash::init(); + CASPAR_LOG(info) << L"Initialized flash module."; + image::init(); CASPAR_LOG(info) << L"Initialized image module."; setup_channels(env::properties()); CASPAR_LOG(info) << L"Initialized channels."; + setup_thumbnail_generation(env::properties()); + setup_controllers(env::properties()); CASPAR_LOG(info) << L"Initialized controllers."; + + setup_osc(env::properties()); + CASPAR_LOG(info) << L"Initialized osc."; } ~implementation() { - ffmpeg::uninit(); - + thumbnail_generator_.reset(); + primary_amcp_server_.reset(); async_servers_.clear(); + destroy_producers_synchronously(); channels_.clear(); + + ffmpeg::uninit(); + } + + void setup_audio(const boost::property_tree::wptree& pt) + { + register_default_channel_layouts(default_channel_layout_repository()); + register_default_mix_configs(default_mix_config_repository()); + + auto channel_layouts = + pt.get_child_optional(L"configuration.audio.channel-layouts"); + auto mix_configs = + pt.get_child_optional(L"configuration.audio.mix-configs"); + + if (channel_layouts) + parse_channel_layouts( + default_channel_layout_repository(), *channel_layouts); + + if (mix_configs) + parse_mix_configs( + default_mix_config_repository(), *mix_configs); } - void setup_channels(const boost::property_tree::ptree& pt) + void setup_channels(const boost::property_tree::wptree& pt) { - using boost::property_tree::ptree; - BOOST_FOREACH(auto& xml_channel, pt.get_child("configuration.channels")) + using boost::property_tree::wptree; + BOOST_FOREACH(auto& xml_channel, pt.get_child(L"configuration.channels")) { - auto format_desc = video_format_desc::get(widen(xml_channel.second.get("video-mode", "PAL"))); + auto format_desc = video_format_desc::get(widen(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 audio_channel_layout = default_channel_layout_repository().get_by_name( + boost::to_upper_copy(xml_channel.second.get(L"channel-layout", L"STEREO"))); - channels_.push_back(make_safe(channels_.size()+1, format_desc, ogl_)); + channels_.push_back(make_safe(channels_.size()+1, format_desc, ogl_, audio_channel_layout)); - int index = 0; - BOOST_FOREACH(auto& xml_consumer, xml_channel.second.get_child("consumers")) - { - try - { - const std::string name = xml_consumer.first; - if(name == "screen") - channels_.back()->output()->add(index++, ogl::create_consumer(xml_consumer.second)); - else if(name == "bluefish") - channels_.back()->output()->add(index++, bluefish::create_consumer(xml_consumer.second)); - else if(name == "decklink") - channels_.back()->output()->add(index++, decklink::create_consumer(xml_consumer.second)); - //else if(name == "file") - // channels_.back()->output()->add(index++, create_ffmpeg_consumer(xml_consumer.second)); - else if(name == "system-audio") - channels_.back()->output()->add(index++, oal::create_consumer()); - else if(name != "") - CASPAR_LOG(warning) << "Invalid consumer: " << widen(name); - } - catch(...) + channels_.back()->monitor_output().attach_parent(monitor_subject_); + channels_.back()->mixer()->set_straight_alpha_output( + xml_channel.second.get(L"straight-alpha-output", false)); + + create_consumers( + xml_channel.second.get_child(L"consumers"), + [&] (const safe_ptr& consumer) { - CASPAR_LOG_CURRENT_EXCEPTION(); - } - } + channels_.back()->output()->add(consumer); + }); + } + + // Dummy diagnostics channel + if(env::properties().get(L"configuration.channel-grid", false)) + channels_.push_back(make_safe(channels_.size()+1, core::video_format_desc::get(core::video_format::x576p2500), ogl_, default_channel_layout_repository().get_by_name(L"STEREO"))); + } + + template + std::vector> create_consumers(const boost::property_tree::wptree& pt) + { + std::vector> consumers; + + create_consumers(pt, [&] (const safe_ptr& consumer) + { + consumers.push_back(dynamic_pointer_cast(consumer)); + }); + + return consumers; + } + + template + void create_consumers(const boost::property_tree::wptree& pt, const Func& on_consumer) + { + BOOST_FOREACH(auto& xml_consumer, pt) + { + try + { + auto name = xml_consumer.first; + + if (name == L"screen") + on_consumer(ogl::create_consumer(xml_consumer.second)); + else if (name == L"bluefish") + on_consumer(bluefish::create_consumer(xml_consumer.second)); + else if (name == L"decklink") + on_consumer(decklink::create_consumer(xml_consumer.second)); + else if (name == L"newtek-ivga") + on_consumer(newtek::create_ivga_consumer(xml_consumer.second)); + else if (name == L"blocking-decklink") + on_consumer(decklink::create_blocking_consumer(xml_consumer.second)); + else if (name == L"file" || name == L"stream") + on_consumer(ffmpeg::create_consumer(xml_consumer.second)); + else if (name == L"system-audio") + on_consumer(portaudio::create_consumer()); + else if (name == L"synchronizing") + on_consumer(make_safe( + create_consumers( + xml_consumer.second))); + else if (name != L"") + CASPAR_LOG(warning) << "Invalid consumer: " << widen(name); + } + catch(...) + { + CASPAR_LOG_CURRENT_EXCEPTION(); + } } } - void setup_controllers(const boost::property_tree::ptree& pt) + void setup_controllers(const boost::property_tree::wptree& pt) { - using boost::property_tree::ptree; - BOOST_FOREACH(auto& xml_controller, pt.get_child("configuration.controllers")) + using boost::property_tree::wptree; + BOOST_FOREACH(auto& xml_controller, pt.get_child(L"configuration.controllers")) { try { - std::string name = xml_controller.first; - std::string protocol = xml_controller.second.get("protocol"); + auto name = xml_controller.first; + auto protocol = xml_controller.second.get(L"protocol"); - if(name == "tcp") + if(name == L"tcp") { - unsigned int port = xml_controller.second.get("port", 5250); + unsigned int port = xml_controller.second.get(L"port", 5250); auto asyncbootstrapper = make_safe(create_protocol(protocol), port); asyncbootstrapper->Start(); async_servers_.push_back(asyncbootstrapper); + + if (!primary_amcp_server_ && boost::iequals(protocol, L"AMCP")) + primary_amcp_server_ = asyncbootstrapper; } else CASPAR_LOG(warning) << "Invalid controller: " << widen(name); @@ -170,24 +272,103 @@ struct server::implementation : boost::noncopyable } } - safe_ptr create_protocol(const std::string& name) const + 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(narrow(address)), + port))); + } + } + + if (primary_amcp_server_) + primary_amcp_server_->add_lifecycle_factory( + [=] (const std::string& ipv4_address) + -> std::shared_ptr + { + using namespace boost::asio::ip; + + return 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(name == "AMCP") - return make_safe(channels_); - else if(name == "CII") + 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( + io_service_manager_.service(), + 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::get(pt.get(L"configuration.thumbnails.video-mode", L"720p2500")), + ogl_, + pt.get(L"configuration.thumbnails.generate-delay-millis", 2000), + &image::write_cropped_png)); + + CASPAR_LOG(info) << L"Initialized thumbnail generator."; + } + + safe_ptr create_protocol(const std::wstring& name) const + { + if(boost::iequals(name, L"AMCP")) + return make_safe(channels_, thumbnail_generator_, shutdown_server_now_); + else if(boost::iequals(name, L"CII")) return make_safe(channels_); - else if(name == "CLOCK") - return make_safe(channels_); + else if(boost::iequals(name, L"CLOCK")) + //return make_safe(channels_); + return make_safe([=] + { + return std::make_shared(channels_); + }); - BOOST_THROW_EXCEPTION(caspar_exception() << arg_name_info("name") << arg_value_info(name) << msg_info("Invalid protocol")); + BOOST_THROW_EXCEPTION(caspar_exception() << arg_name_info("name") << arg_value_info(narrow(name)) << msg_info("Invalid protocol")); } }; -server::server() : impl_(new implementation()){} +server::server(boost::promise& shutdown_server_now) : impl_(new implementation(shutdown_server_now)){} const std::vector> server::get_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