]> git.sesse.net Git - casparcg/blob - shell/server.cpp
072cd82e194f3ea6a3d2199a6c2ba3d87fde63e8
[casparcg] / shell / server.cpp
1 /*
2 * Copyright (c) 2011 Sveriges Television AB <info@casparcg.com>
3 *
4 * This file is part of CasparCG (www.casparcg.com).
5 *
6 * CasparCG is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * CasparCG is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with CasparCG. If not, see <http://www.gnu.org/licenses/>.
18 *
19 * Author: Robert Nagy, ronag89@gmail.com
20 */
21 #include "stdafx.h"
22
23 #include "server.h"
24
25 #include <accelerator/accelerator.h>
26
27 #include <common/env.h>
28 #include <common/except.h>
29 #include <common/utf.h>
30 #include <common/memory.h>
31 #include <common/polling_filesystem_monitor.h>
32
33 #include <core/video_channel.h>
34 #include <core/video_format.h>
35 #include <core/producer/stage.h>
36 #include <core/producer/frame_producer.h>
37 #include <core/producer/scene/scene_producer.h>
38 #include <core/producer/scene/xml_scene_producer.h>
39 #include <core/producer/text/text_producer.h>
40 #include <core/consumer/output.h>
41 #include <core/thumbnail_generator.h>
42 #include <core/producer/media_info/media_info.h>
43 #include <core/producer/media_info/media_info_repository.h>
44 #include <core/producer/media_info/in_memory_media_info_repository.h>
45 #include <core/producer/cg_proxy.h>
46 #include <core/diagnostics/subject_diagnostics.h>
47 #include <core/diagnostics/call_context.h>
48 #include <core/diagnostics/osd_graph.h>
49 #include <core/system_info_provider.h>
50
51 #include <modules/bluefish/bluefish.h>
52 #include <modules/decklink/decklink.h>
53 #include <modules/ffmpeg/ffmpeg.h>
54 #include <modules/flash/flash.h>
55 #include <modules/oal/oal.h>
56 #include <modules/screen/screen.h>
57 #include <modules/image/image.h>
58 #include <modules/image/consumer/image_consumer.h>
59 #include <modules/psd/psd_scene_producer.h>
60
61 #include <protocol/asio/io_service_manager.h>
62 #include <protocol/amcp/AMCPProtocolStrategy.h>
63 #include <protocol/cii/CIIProtocolStrategy.h>
64 #include <protocol/clk/CLKProtocolStrategy.h>
65 #include <protocol/util/AsyncEventServer.h>
66 #include <protocol/util/strategy_adapters.h>
67 #include <protocol/osc/client.h>
68
69 #include <boost/algorithm/string.hpp>
70 #include <boost/thread.hpp>
71 #include <boost/lexical_cast.hpp>
72 #include <boost/property_tree/ptree.hpp>
73 #include <boost/property_tree/xml_parser.hpp>
74
75 #include <tbb/atomic.h>
76
77 #include <future>
78
79 namespace caspar {
80
81 using namespace core;
82 using namespace protocol;
83
84 struct server::impl : boost::noncopyable
85 {
86         protocol::asio::io_service_manager                                      io_service_manager_;
87         spl::shared_ptr<monitor::subject>                                       monitor_subject_;
88         spl::shared_ptr<monitor::subject>                                       diag_subject_                                   = core::diagnostics::get_or_create_subject();
89         accelerator::accelerator                                                        accelerator_;
90         std::vector<spl::shared_ptr<IO::AsyncEventServer>>      async_servers_;
91         std::shared_ptr<IO::AsyncEventServer>                           primary_amcp_server_;
92         osc::client                                                                                     osc_client_;
93         std::vector<std::shared_ptr<void>>                                      predefined_osc_subscriptions_;
94         std::vector<spl::shared_ptr<video_channel>>                     channels_;
95         spl::shared_ptr<media_info_repository>                          media_info_repo_;
96         boost::thread                                                                           initial_media_info_thread_;
97         spl::shared_ptr<system_info_provider_repository>        system_info_provider_repo_;
98         spl::shared_ptr<core::cg_producer_registry>                     cg_registry_;
99         tbb::atomic<bool>                                                                       running_;
100         std::shared_ptr<thumbnail_generator>                            thumbnail_generator_;
101         std::promise<bool>&                                                                     shutdown_server_now_;
102
103         explicit impl(std::promise<bool>& shutdown_server_now)          
104                 : accelerator_(env::properties().get(L"configuration.accelerator", L"auto"))
105                 , osc_client_(io_service_manager_.service())
106                 , media_info_repo_(create_in_memory_media_info_repository())
107                 , shutdown_server_now_(shutdown_server_now)
108         {
109                 running_ = false;
110                 core::diagnostics::osd::register_sink();
111                 diag_subject_->attach_parent(monitor_subject_);
112
113                 ffmpeg::init(media_info_repo_, system_info_provider_repo_);
114                 CASPAR_LOG(info) << L"Initialized ffmpeg module.";
115                                                           
116                 bluefish::init(system_info_provider_repo_);
117                 CASPAR_LOG(info) << L"Initialized bluefish module.";
118                                                           
119                 decklink::init(system_info_provider_repo_);       
120                 CASPAR_LOG(info) << L"Initialized decklink module.";
121                                                                                                                   
122                 oal::init();              
123                 CASPAR_LOG(info) << L"Initialized oal module.";
124                                                           
125                 screen::init();           
126                 CASPAR_LOG(info) << L"Initialized ogl module.";
127
128                 image::init(media_info_repo_, system_info_provider_repo_);
129                 CASPAR_LOG(info) << L"Initialized image module.";
130
131                 flash::init(media_info_repo_, system_info_provider_repo_, cg_registry_);
132                 CASPAR_LOG(info) << L"Initialized flash module.";
133
134                 psd::init();              
135                 CASPAR_LOG(info) << L"Initialized psd module.";
136
137                 core::text::init();
138
139                 register_producer_factory(&core::scene::create_dummy_scene_producer);
140                 register_producer_factory(&core::scene::create_xml_scene_producer);
141         }
142
143         void start()
144         {
145                 running_ = true;
146
147                 setup_channels(env::properties());
148                 CASPAR_LOG(info) << L"Initialized channels.";
149
150                 setup_thumbnail_generation(env::properties());
151                 CASPAR_LOG(info) << L"Initialized thumbnail generator.";
152
153                 setup_controllers(env::properties());
154                 CASPAR_LOG(info) << L"Initialized controllers.";
155
156                 setup_osc(env::properties());
157                 CASPAR_LOG(info) << L"Initialized osc.";
158
159                 start_initial_media_info_scan();
160                 CASPAR_LOG(info) << L"Started initial media information retrieval.";
161         }
162
163         ~impl()
164         {
165                 if (running_)
166                 {
167                         running_ = false;
168                         initial_media_info_thread_.join();
169                 }
170
171                 thumbnail_generator_.reset();
172                 primary_amcp_server_.reset();
173                 async_servers_.clear();
174                 channels_.clear();
175
176                 boost::this_thread::sleep(boost::posix_time::milliseconds(500));
177                 //Sleep(500); // HACK: Wait for asynchronous destruction of producers and consumers.
178
179                 image::uninit();
180                 ffmpeg::uninit();
181                 core::diagnostics::osd::shutdown();
182         }
183                                 
184         void setup_channels(const boost::property_tree::wptree& pt)
185         {   
186                 using boost::property_tree::wptree;
187                 for (auto& xml_channel : pt.get_child(L"configuration.channels"))
188                 {               
189                         auto format_desc = video_format_desc(xml_channel.second.get(L"video-mode", L"PAL"));            
190                         if(format_desc.format == video_format::invalid)
191                                 CASPAR_THROW_EXCEPTION(caspar_exception() << msg_info("Invalid video-mode."));
192                         
193                         auto channel = spl::make_shared<video_channel>(static_cast<int>(channels_.size()+1), format_desc, accelerator_.create_image_mixer());
194
195                         core::diagnostics::scoped_call_context save;
196                         core::diagnostics::call_context::for_thread().video_channel = channel->index();
197                         
198                         for (auto& xml_consumer : xml_channel.second.get_child(L"consumers"))
199                         {
200                                 try
201                                 {
202                                         auto name = xml_consumer.first;
203                                         
204                                         if (name != L"<xmlcomment>")
205                                                 channel->output().add(create_consumer(name, xml_consumer.second, &channel->stage()));
206                                 }
207                                 catch(...)
208                                 {
209                                         CASPAR_LOG_CURRENT_EXCEPTION();
210                                 }
211                         }               
212
213                     channel->monitor_output().attach_parent(monitor_subject_);
214                         channels_.push_back(channel);
215                 }
216
217                 // Dummy diagnostics channel
218                 if(env::properties().get(L"configuration.channel-grid", false))
219                         channels_.push_back(spl::make_shared<video_channel>(static_cast<int>(channels_.size()+1), core::video_format_desc(core::video_format::x576p2500), accelerator_.create_image_mixer()));
220         }
221
222         void setup_osc(const boost::property_tree::wptree& pt)
223         {               
224                 using boost::property_tree::wptree;
225                 using namespace boost::asio::ip;
226
227                 monitor_subject_->attach_parent(osc_client_.sink());
228                 
229                 auto default_port =
230                                 pt.get<unsigned short>(L"configuration.osc.default-port", 6250);
231                 auto predefined_clients =
232                                 pt.get_child_optional(L"configuration.osc.predefined-clients");
233
234                 if (predefined_clients)
235                 {
236                         for (auto& predefined_client : *predefined_clients)
237                         {
238                                 const auto address =
239                                                 predefined_client.second.get<std::wstring>(L"address");
240                                 const auto port =
241                                                 predefined_client.second.get<unsigned short>(L"port");
242                                 predefined_osc_subscriptions_.push_back(
243                                                 osc_client_.get_subscription_token(udp::endpoint(
244                                                                 address_v4::from_string(u8(address)),
245                                                                 port)));
246                         }
247                 }
248
249                 if (primary_amcp_server_)
250                         primary_amcp_server_->add_client_lifecycle_object_factory(
251                                         [=] (const std::string& ipv4_address)
252                                                         -> std::pair<std::wstring, std::shared_ptr<void>>
253                                         {
254                                                 using namespace boost::asio::ip;
255
256                                                 return std::make_pair(
257                                                                 std::wstring(L"osc_subscribe"),
258                                                                 osc_client_.get_subscription_token(
259                                                                                 udp::endpoint(
260                                                                                                 address_v4::from_string(
261                                                                                                                 ipv4_address),
262                                                                                                 default_port)));
263                                         });
264         }
265
266         void setup_thumbnail_generation(const boost::property_tree::wptree& pt)
267         {
268                 if (!pt.get(L"configuration.thumbnails.generate-thumbnails", true))
269                         return;
270
271                 auto scan_interval_millis = pt.get(L"configuration.thumbnails.scan-interval-millis", 5000);
272
273                 polling_filesystem_monitor_factory monitor_factory(scan_interval_millis);
274                 thumbnail_generator_.reset(new thumbnail_generator(
275                         monitor_factory, 
276                         env::media_folder(),
277                         env::thumbnails_folder(),
278                         pt.get(L"configuration.thumbnails.width", 256),
279                         pt.get(L"configuration.thumbnails.height", 144),
280                         core::video_format_desc(pt.get(L"configuration.thumbnails.video-mode", L"720p2500")),
281                         accelerator_.create_image_mixer(),
282                         pt.get(L"configuration.thumbnails.generate-delay-millis", 2000),
283                         &image::write_cropped_png,
284                         media_info_repo_));
285
286                 CASPAR_LOG(info) << L"Initialized thumbnail generator.";
287         }
288                 
289         void setup_controllers(const boost::property_tree::wptree& pt)
290         {               
291                 using boost::property_tree::wptree;
292                 for (auto& xml_controller : pt.get_child(L"configuration.controllers"))
293                 {
294                         try
295                         {
296                                 auto name = xml_controller.first;
297                                 auto protocol = xml_controller.second.get<std::wstring>(L"protocol");   
298
299                                 if(name == L"tcp")
300                                 {                                       
301                                         unsigned int port = xml_controller.second.get(L"port", 5250);
302                                         auto asyncbootstrapper = spl::make_shared<IO::AsyncEventServer>(create_protocol(protocol), port);
303                                         async_servers_.push_back(asyncbootstrapper);
304
305                                         if (!primary_amcp_server_ && boost::iequals(protocol, L"AMCP"))
306                                                 primary_amcp_server_ = asyncbootstrapper;
307                                 }
308                                 else
309                                         CASPAR_LOG(warning) << "Invalid controller: " << name;  
310                         }
311                         catch(...)
312                         {
313                                 CASPAR_LOG_CURRENT_EXCEPTION();
314                         }
315                 }
316         }
317
318         IO::protocol_strategy_factory<char>::ptr create_protocol(const std::wstring& name) const
319         {
320                 using namespace IO;
321
322                 if(boost::iequals(name, L"AMCP"))
323                         return wrap_legacy_protocol("\r\n", spl::make_shared<amcp::AMCPProtocolStrategy>(
324                                         channels_,
325                                         thumbnail_generator_,
326                                         media_info_repo_,
327                                         system_info_provider_repo_,
328                                         cg_registry_,
329                                         shutdown_server_now_));
330                 else if(boost::iequals(name, L"CII"))
331                         return wrap_legacy_protocol("\r\n", spl::make_shared<cii::CIIProtocolStrategy>(channels_, cg_registry_));
332                 else if(boost::iequals(name, L"CLOCK"))
333                         return spl::make_shared<to_unicode_adapter_factory>(
334                                         "ISO-8859-1",
335                                         spl::make_shared<CLK::clk_protocol_strategy_factory>(channels_, cg_registry_));
336                 
337                 CASPAR_THROW_EXCEPTION(caspar_exception() << arg_name_info(L"name") << arg_value_info(name) << msg_info(L"Invalid protocol"));
338         }
339
340         void start_initial_media_info_scan()
341         {
342                 initial_media_info_thread_ = boost::thread([this]
343                 {
344                         for (boost::filesystem::wrecursive_directory_iterator iter(env::media_folder()), end; iter != end; ++iter)
345                         {
346                                 if (running_)
347                                 {
348                                         if (boost::filesystem::is_regular_file(iter->path()))
349                                                 media_info_repo_->get(iter->path().wstring());
350                                 }
351                                 else
352                                 {
353                                         CASPAR_LOG(info) << L"Initial media information retrieval aborted.";
354                                         return;
355                                 }
356                         }
357
358                         CASPAR_LOG(info) << L"Initial media information retrieval finished.";
359                 });
360         }
361 };
362
363 server::server(std::promise<bool>& shutdown_server_now) : impl_(new impl(shutdown_server_now)){}
364 void server::start() { impl_->start(); }
365 const std::vector<spl::shared_ptr<video_channel>> server::channels() const
366 {
367         return impl_->channels_;
368 }
369 std::shared_ptr<core::thumbnail_generator> server::get_thumbnail_generator() const {return impl_->thumbnail_generator_; }
370 spl::shared_ptr<media_info_repository> server::get_media_info_repo() const { return impl_->media_info_repo_; }
371 spl::shared_ptr<core::system_info_provider_repository> server::get_system_info_provider_repo() const { return impl_->system_info_provider_repo_; }
372 spl::shared_ptr<core::cg_producer_registry> server::get_cg_registry() const { return impl_->cg_registry_; }
373 core::monitor::subject& server::monitor_output() { return *impl_->monitor_subject_; }
374
375 }