]> git.sesse.net Git - casparcg/blob - shell/main.cpp
Merge branch '2.1.0' of https://github.com/CasparCG/Server into 2.1.0
[casparcg] / shell / main.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
22 // tbbmalloc_proxy: 
23 // Replace the standard memory allocation routines in Microsoft* C/C++ RTL 
24 // (malloc/free, global new/delete, etc.) with the TBB memory allocator. 
25
26 #include "stdafx.h"
27
28 #ifdef _DEBUG
29         #define _CRTDBG_MAP_ALLOC
30         #include <stdlib.h>
31         #include <crtdbg.h>
32 #else
33         #include <tbb/tbbmalloc_proxy.h>
34 #endif
35
36 #include "resource.h"
37
38 #include "server.h"
39
40 #include <common/os/windows/windows.h>
41 #include <winnt.h>
42 #include <mmsystem.h>
43 #include <atlbase.h>
44
45 #include <protocol/util/strategy_adapters.h>
46 #include <protocol/amcp/AMCPProtocolStrategy.h>
47 #include <protocol/osc/server.h>
48
49 #include <modules/bluefish/bluefish.h>
50 #include <modules/decklink/decklink.h>
51 #include <modules/flash/flash.h>
52 #include <modules/ffmpeg/ffmpeg.h>
53 #include <modules/image/image.h>
54
55 #include <common/env.h>
56 #include <common/except.h>
57 #include <common/except.h>
58 #include <common/log.h>
59 #include <common/gl/gl_check.h>
60 #include <common/os/windows/current_version.h>
61 #include <common/os/windows/system_info.h>
62
63 #include <tbb/task_scheduler_init.h>
64 #include <tbb/task_scheduler_observer.h>
65
66 #include <boost/property_tree/detail/file_parser_error.hpp>
67 #include <boost/property_tree/xml_parser.hpp>
68 #include <boost/foreach.hpp>
69 #include <boost/locale.hpp>
70 #include <boost/algorithm/string/predicate.hpp>
71
72 #include <signal.h>
73
74 using namespace caspar;
75         
76 // NOTE: This is needed in order to make CComObject work since this is not a real ATL project.
77 CComModule _AtlModule;
78 extern __declspec(selectany) CAtlModule* _pAtlModule = &_AtlModule;
79
80 void change_icon( const HICON hNewIcon )
81 {
82    auto hMod = ::LoadLibrary(L"Kernel32.dll"); 
83    typedef DWORD(__stdcall *SCI)(HICON);
84    auto pfnSetConsoleIcon = reinterpret_cast<SCI>(::GetProcAddress(hMod, "SetConsoleIcon")); 
85    pfnSetConsoleIcon(hNewIcon); 
86    ::FreeLibrary(hMod);
87 }
88
89 void setup_global_locale()
90 {
91         boost::locale::generator gen;
92         gen.categories(boost::locale::codepage_facet);
93
94         std::locale::global(gen(""));
95 }
96
97 void setup_console_window()
98 {        
99         auto hOut = GetStdHandle(STD_OUTPUT_HANDLE);
100
101         // Disable close button in console to avoid shutdown without cleanup.
102         EnableMenuItem(GetSystemMenu(GetConsoleWindow(), FALSE), SC_CLOSE , MF_GRAYED);
103         DrawMenuBar(GetConsoleWindow());
104         //SetConsoleCtrlHandler(HandlerRoutine, true);
105
106         // Configure console size and position.
107         auto coord = GetLargestConsoleWindowSize(hOut);
108         coord.X /= 2;
109
110         SetConsoleScreenBufferSize(hOut, coord);
111
112         SMALL_RECT DisplayArea = {0, 0, 0, 0};
113         DisplayArea.Right = coord.X-1;
114         DisplayArea.Bottom = (coord.Y-1)/2;
115         SetConsoleWindowInfo(hOut, TRUE, &DisplayArea);
116                  
117         change_icon(::LoadIcon(GetModuleHandle(0), MAKEINTRESOURCE(101)));
118
119         // Set console title.
120         std::wstringstream str;
121         str << "CasparCG Server " << env::version() << L" x64 ";
122 #ifdef COMPILE_RELEASE
123         str << " Release";
124 #elif  COMPILE_PROFILE
125         str << " Profile";
126 #elif  COMPILE_DEVELOP
127         str << " Develop";
128 #elif  COMPILE_DEBUG
129         str << " Debug";
130 #endif
131         SetConsoleTitle(str.str().c_str());
132 }
133
134 void print_info()
135 {
136         CASPAR_LOG(info) << L"############################################################################";
137         CASPAR_LOG(info) << L"CasparCG Server is distributed by the Swedish Broadcasting Corporation (SVT)";
138         CASPAR_LOG(info) << L"under the GNU General Public License GPLv3 or higher.";
139         CASPAR_LOG(info) << L"Please see LICENSE.TXT for details.";
140         CASPAR_LOG(info) << L"http://www.casparcg.com/";
141         CASPAR_LOG(info) << L"############################################################################";
142         CASPAR_LOG(info) << L"Starting CasparCG Video and Graphics Playout Server " << env::version();
143         CASPAR_LOG(info) << L"on " << win_product_name() << L" " << win_sp_version();
144         CASPAR_LOG(info) << cpu_info();
145         CASPAR_LOG(info) << system_product_name();
146         
147         CASPAR_LOG(info) << L"Decklink " << decklink::version();
148         BOOST_FOREACH(auto device, decklink::device_list())
149                 CASPAR_LOG(info) << L" - " << device;   
150                 
151         CASPAR_LOG(info) << L"Bluefish " << bluefish::version();
152         BOOST_FOREACH(auto device, bluefish::device_list())
153                 CASPAR_LOG(info) << L" - " << device;   
154         
155         CASPAR_LOG(info) << L"Flash "                   << flash::version();
156         CASPAR_LOG(info) << L"FreeImage "               << image::version();
157         CASPAR_LOG(info) << L"FFMPEG-avcodec "  << ffmpeg::avcodec_version();
158         CASPAR_LOG(info) << L"FFMPEG-avformat " << ffmpeg::avformat_version();
159         CASPAR_LOG(info) << L"FFMPEG-avfilter " << ffmpeg::avfilter_version();
160         CASPAR_LOG(info) << L"FFMPEG-avutil "   << ffmpeg::avutil_version();
161         CASPAR_LOG(info) << L"FFMPEG-swscale "  << ffmpeg::swscale_version();
162 }
163
164 LONG WINAPI UserUnhandledExceptionFilter(EXCEPTION_POINTERS* info)
165 {
166         try
167         {
168                 CASPAR_LOG(fatal) << L"#######################\n UNHANDLED EXCEPTION: \n" 
169                         << L"Adress:" << info->ExceptionRecord->ExceptionAddress << L"\n"
170                         << L"Code:" << info->ExceptionRecord->ExceptionCode << L"\n"
171                         << L"Flag:" << info->ExceptionRecord->ExceptionFlags << L"\n"
172                         << L"Info:" << info->ExceptionRecord->ExceptionInformation << L"\n"
173                         << L"Continuing execution. \n#######################";
174
175                 CASPAR_LOG_CALL_STACK();
176         }
177         catch(...){}
178
179     return EXCEPTION_CONTINUE_EXECUTION;
180 }
181
182 void run()
183 {
184         // Create server object which initializes channels, protocols and controllers.
185         server caspar_server;
186                                 
187         auto server = spl::make_shared<protocol::osc::server>(5253);
188         caspar_server.subscribe(server);
189                                                 
190         //auto console_obs = reactive::make_observer([](const monitor::event& e)
191         //{
192         //      std::stringstream str;
193         //      str << e;
194         //      CASPAR_LOG(trace) << str.str().c_str();
195         //});
196
197         //caspar_server.subscribe(console_obs);
198                                                 
199         // Create a dummy client which prints amcp responses to console.
200         auto console_client = spl::make_shared<IO::ConsoleClientInfo>();
201         
202         // Create a amcp parser for console commands.
203         //protocol::amcp::AMCPProtocolStrategy amcp(caspar_server.channels());
204         auto amcp = spl::make_shared<caspar::IO::delimiter_based_chunking_strategy_factory<wchar_t>>(L"\r\n", spl::make_shared<caspar::IO::legacy_strategy_adapter_factory>(spl::make_shared<protocol::amcp::AMCPProtocolStrategy>(caspar_server.channels())))->create(console_client);
205
206         std::wstring wcmd;
207         while(true)
208         {
209                 std::getline(std::wcin, wcmd); // TODO: It's blocking...
210                                 
211                 //boost::to_upper(wcmd);
212
213                 if(boost::iequals(wcmd, L"EXIT") || boost::iequals(wcmd, L"Q") || boost::iequals(wcmd, L"QUIT") || boost::iequals(wcmd, L"BYE"))
214                         break;
215
216                 try
217                 {
218                         // This is just dummy code for testing.
219                         if(wcmd.substr(0, 1) == L"1")
220                                 wcmd = L"LOADBG 1-1 " + wcmd.substr(1, wcmd.length()-1) + L" SLIDE 100 LOOP \r\nPLAY 1-1";
221                         else if(wcmd.substr(0, 1) == L"2")
222                                 wcmd = L"MIXER 1-0 VIDEO IS_KEY 1";
223                         else if(wcmd.substr(0, 1) == L"3")
224                                 wcmd = L"CG 1-2 ADD 1 BBTELEFONARE 1";
225                         else if(wcmd.substr(0, 1) == L"4")
226                                 wcmd = L"PLAY 1-1 DV FILTER yadif=1:-1 LOOP";
227                         else if(wcmd.substr(0, 1) == L"5")
228                         {
229                                 auto file = wcmd.substr(2, wcmd.length()-1);
230                                 wcmd = L"PLAY 1-1 " + file + L" LOOP\r\n" 
231                                                 L"PLAY 1-2 " + file + L" LOOP\r\n" 
232                                                 L"PLAY 1-3 " + file + L" LOOP\r\n"
233                                                 L"PLAY 2-1 " + file + L" LOOP\r\n" 
234                                                 L"PLAY 2-2 " + file + L" LOOP\r\n" 
235                                                 L"PLAY 2-3 " + file + L" LOOP\r\n";
236                         }
237                         else if(wcmd.substr(0, 1) == L"7")
238                         {
239                                 wcmd = L"";
240                                 wcmd += L"CLEAR 1\r\n";
241                                 wcmd += L"MIXER 1 CLEAR\r\n";
242                                 wcmd += L"PLAY 1-0 GREEN\r\n";
243                                 wcmd += L"PLAY 1-1 BLUE\r\n";
244                                 wcmd += L"CG 1-2 ADD 1 ECS_TEST 1\r\n";
245                                 wcmd += L"MIXER 1-2 FILL 0 -1 1 2\r\n";
246                         }
247                         else if(wcmd.substr(0, 1) == L"8")
248                         {
249                                 wcmd = L"";
250                                 wcmd += L"MIXER 1-1 FILL 0.0 0.5 1.0 1.0 500 linear DEFER\r\n";
251                                 wcmd += L"MIXER 1-2 FILL 0.0 0.0 1.0 1.0 500 linear DEFER\r\n";
252                                 wcmd += L"MIXER 1 COMMIT\r\n";
253                         }
254                         else if(wcmd.substr(0, 1) == L"X")
255                         {
256                                 int num = 0;
257                                 std::wstring file;
258                                 try
259                                 {
260                                         num = boost::lexical_cast<int>(wcmd.substr(1, 2));
261                                         file = wcmd.substr(4, wcmd.length()-1);
262                                 }
263                                 catch(...)
264                                 {
265                                         num = boost::lexical_cast<int>(wcmd.substr(1, 1));
266                                         file = wcmd.substr(3, wcmd.length()-1);
267                                 }
268
269                                 int n = 0;
270                                 int num2 = num;
271                                 while(num2 > 0)
272                                 {
273                                         num2 >>= 1;
274                                         n++;
275                                 }
276
277                                 wcmd = L"MIXER 1 GRID " + boost::lexical_cast<std::wstring>(n);
278
279                                 for(int i = 1; i <= num; ++i)
280                                         wcmd += L"\r\nPLAY 1-" + boost::lexical_cast<std::wstring>(i) + L" " + file + L" LOOP";// + L" SLIDE 100 LOOP";
281                         }
282                 }
283                 catch (...)
284                 {
285                         CASPAR_LOG_CURRENT_EXCEPTION();
286                         continue;
287                 }
288
289                 wcmd += L"\r\n";
290                 amcp->parse(wcmd);
291         }       
292         CASPAR_LOG(info) << "Successfully shutdown CasparCG Server.";
293 }
294
295 void on_abort(int)
296 {
297         CASPAR_THROW_EXCEPTION(invalid_operation() << msg_info("abort called"));
298 }
299
300 int main(int argc, wchar_t* argv[])
301 {       
302         SetUnhandledExceptionFilter(UserUnhandledExceptionFilter);
303         signal(SIGABRT, on_abort);
304
305         setup_global_locale();
306
307         std::wcout << L"Type \"q\" to close application." << std::endl;
308         
309         // Set debug mode.
310         #ifdef _DEBUG
311                 HANDLE hLogFile;
312                 hLogFile = CreateFile(L"crt_log.txt", GENERIC_WRITE, FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
313                 std::shared_ptr<void> crt_log(nullptr, [](HANDLE h){::CloseHandle(h);});
314
315                 _CrtSetDbgFlag (_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);
316                 _CrtSetReportMode(_CRT_WARN,    _CRTDBG_MODE_FILE);
317                 _CrtSetReportFile(_CRT_WARN,    hLogFile);
318                 _CrtSetReportMode(_CRT_ERROR,   _CRTDBG_MODE_FILE);
319                 _CrtSetReportFile(_CRT_ERROR,   hLogFile);
320                 _CrtSetReportMode(_CRT_ASSERT,  _CRTDBG_MODE_FILE);
321                 _CrtSetReportFile(_CRT_ASSERT,  hLogFile);
322         #endif
323
324         // Increase process priotity.
325         SetPriorityClass(GetCurrentProcess(), ABOVE_NORMAL_PRIORITY_CLASS);
326
327         // Install structured exception handler.
328         win32_exception::install_handler();
329                                 
330         // Increase time precision. This will increase accuracy of function like Sleep(1) from 10 ms to 1 ms.
331         struct inc_prec
332         {
333                 inc_prec(){timeBeginPeriod(1);}
334                 ~inc_prec(){timeEndPeriod(1);}
335         } inc_prec;     
336
337         // Install SEH into all tbb threads.
338         struct tbb_thread_installer : public tbb::task_scheduler_observer
339         {
340                 tbb_thread_installer(){observe(true);}
341                 void on_scheduler_entry(bool is_worker)
342                 {
343                         //detail::SetThreadName(GetCurrentThreadId(), "tbb-worker-thread");
344                         win32_exception::install_handler();
345                 }
346         } tbb_thread_installer;
347
348         tbb::task_scheduler_init init;
349         
350         try 
351         {
352                 // Configure environment properties from configuration.
353                 env::configure(L"casparcg.config");
354                                 
355                 log::set_log_level(env::properties().get(L"configuration.log-level", L"debug"));
356
357         #ifdef _DEBUG
358                 if(env::properties().get(L"configuration.debugging.remote", false))
359                         MessageBox(nullptr, TEXT("Now is the time to connect for remote debugging..."), TEXT("Debug"), MB_OK | MB_TOPMOST);
360         #endif   
361
362                 // Start logging to file.
363                 log::add_file_sink(env::log_folder());                  
364                 std::wcout << L"Logging [info] or higher severity to " << env::log_folder() << std::endl << std::endl;
365                 
366                 // Setup console window.
367                 setup_console_window();
368
369                 // Print environment information.
370                 print_info();
371                 
372                 std::wstringstream str;
373                 boost::property_tree::xml_writer_settings<wchar_t> w(' ', 3);
374                 boost::property_tree::write_xml(str, env::properties(), w);
375                 CASPAR_LOG(info) << L"casparcg.config:\n-----------------------------------------\n" << str.str().c_str() << L"-----------------------------------------";
376                 
377                 run();
378                 
379                 system("pause");        
380         }
381         catch(boost::property_tree::file_parser_error&)
382         {
383                 CASPAR_LOG_CURRENT_EXCEPTION();
384                 CASPAR_LOG(fatal) << L"Unhandled configuration error in main thread. Please check the configuration file (casparcg.config) for errors.";
385                 system("pause");        
386         }
387         catch(...)
388         {
389                 CASPAR_LOG_CURRENT_EXCEPTION();
390                 CASPAR_LOG(fatal) << L"Unhandled exception in main thread. Please report this error on the CasparCG forums (www.casparcg.com/forum).";
391                 Sleep(1000);
392                 std::wcout << L"\n\nCasparCG will automatically shutdown. See the log file located at the configured log-file folder for more information.\n\n";
393                 Sleep(4000);
394         }               
395         
396         return 0;
397 }