]> git.sesse.net Git - casparcg/blob - shell/main.cpp
2.0.2: Updated file info headers.
[casparcg] / shell / main.cpp
1 /*\r
2 * Copyright (c) 2011 Sveriges Television AB <info@casparcg.com>\r
3 *\r
4 * This file is part of CasparCG (www.casparcg.com).\r
5 *\r
6 * CasparCG is free software: you can redistribute it and/or modify\r
7 * it under the terms of the GNU General Public License as published by\r
8 * the Free Software Foundation, either version 3 of the License, or\r
9 * (at your option) any later version.\r
10 *\r
11 * CasparCG is distributed in the hope that it will be useful,\r
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
14 * GNU General Public License for more details.\r
15 *\r
16 * You should have received a copy of the GNU General Public License\r
17 * along with CasparCG. If not, see <http://www.gnu.org/licenses/>.\r
18 *\r
19 * Author: Robert Nagy, ronag89@gmail.com\r
20 */\r
21 \r
22 // tbbmalloc_proxy: \r
23 // Replace the standard memory allocation routines in Microsoft* C/C++ RTL \r
24 // (malloc/free, global new/delete, etc.) with the TBB memory allocator. \r
25 #include <tbb/tbbmalloc_proxy.h>\r
26 \r
27 #include "resource.h"\r
28 \r
29 #include "server.h"\r
30 \r
31 #ifdef _DEBUG\r
32         #define _CRTDBG_MAP_ALLOC\r
33         #include <stdlib.h>\r
34         #include <crtdbg.h>\r
35 #endif\r
36 \r
37 #define NOMINMAX\r
38 #define WIN32_LEAN_AND_MEAN\r
39 \r
40 #include <windows.h>\r
41 #include <winnt.h>\r
42 #include <mmsystem.h>\r
43 #include <atlbase.h>\r
44 \r
45 #include <protocol/amcp/AMCPProtocolStrategy.h>\r
46 \r
47 #include <modules/bluefish/bluefish.h>\r
48 #include <modules/decklink/decklink.h>\r
49 #include <modules/flash/flash.h>\r
50 #include <modules/ffmpeg/ffmpeg.h>\r
51 #include <modules/image/image.h>\r
52 \r
53 #include <common/env.h>\r
54 #include <common/exception/win32_exception.h>\r
55 #include <common/exception/exceptions.h>\r
56 #include <common/log/log.h>\r
57 #include <common/gl/gl_check.h>\r
58 #include <common/os/windows/current_version.h>\r
59 #include <common/os/windows/system_info.h>\r
60 \r
61 #include <core/mixer/gpu/ogl_device.h>\r
62 \r
63 #include <tbb/task_scheduler_init.h>\r
64 #include <tbb/task_scheduler_observer.h>\r
65 \r
66 #include <boost/property_tree/detail/file_parser_error.hpp>\r
67 #include <boost/property_tree/xml_parser.hpp>\r
68 \r
69 #include <algorithm>\r
70 \r
71 struct application_state\r
72 {\r
73         enum type\r
74         {\r
75                 running,\r
76                 shutdown,\r
77                 pause_and_shutdown\r
78         };\r
79 };\r
80 \r
81 boost::condition_variable       shutdown_cond;\r
82 boost::mutex                            shutdown_mut;\r
83 tbb::atomic<int>                        shutdown_event;\r
84 \r
85 // NOTE: This is needed in order to make CComObject work since this is not a real ATL project.\r
86 CComModule _AtlModule;\r
87 extern __declspec(selectany) CAtlModule* _pAtlModule = &_AtlModule;\r
88 \r
89 void change_icon( const HICON hNewIcon )\r
90 {\r
91    auto hMod = ::LoadLibrary(L"Kernel32.dll"); \r
92    typedef DWORD(__stdcall *SCI)(HICON);\r
93    auto pfnSetConsoleIcon = reinterpret_cast<SCI>(::GetProcAddress(hMod, "SetConsoleIcon")); \r
94    pfnSetConsoleIcon(hNewIcon); \r
95    ::FreeLibrary(hMod);\r
96 }\r
97 \r
98 BOOL WINAPI HandlerRoutine(__in  DWORD dwCtrlType)\r
99 {\r
100         switch(dwCtrlType)\r
101         {\r
102         case CTRL_CLOSE_EVENT:\r
103         case CTRL_SHUTDOWN_EVENT:\r
104                 shutdown_event = application_state::shutdown;\r
105                 shutdown_cond.notify_all();\r
106                 return true;\r
107         }\r
108         return false;\r
109 }\r
110 \r
111 void setup_console_window()\r
112 {        \r
113         auto hOut = GetStdHandle(STD_OUTPUT_HANDLE);\r
114 \r
115         // Disable close button in console to avoid shutdown without cleanup.\r
116         //EnableMenuItem(GetSystemMenu(GetConsoleWindow(), FALSE), SC_CLOSE , MF_GRAYED);\r
117         //DrawMenuBar(GetConsoleWindow());\r
118         SetConsoleCtrlHandler(HandlerRoutine, true);\r
119 \r
120         // Configure console size and position.\r
121         auto coord = GetLargestConsoleWindowSize(hOut);\r
122         coord.X /= 2;\r
123 \r
124         SetConsoleScreenBufferSize(hOut, coord);\r
125 \r
126         SMALL_RECT DisplayArea = {0, 0, 0, 0};\r
127         DisplayArea.Right = coord.X-1;\r
128         DisplayArea.Bottom = (coord.Y-1)/2;\r
129         SetConsoleWindowInfo(hOut, TRUE, &DisplayArea);\r
130                  \r
131         change_icon(::LoadIcon(GetModuleHandle(0), MAKEINTRESOURCE(101)));\r
132 \r
133         // Set console title.\r
134         std::wstringstream str;\r
135         str << "CasparCG Server " << caspar::env::version();\r
136 #ifdef COMPILE_RELEASE\r
137         str << " Release";\r
138 #elif  COMPILE_PROFILE\r
139         str << " Profile";\r
140 #elif  COMPILE_DEVELOP\r
141         str << " Develop";\r
142 #elif  COMPILE_DEBUG\r
143         str << " Debug";\r
144 #endif\r
145         SetConsoleTitle(str.str().c_str());\r
146 }\r
147 \r
148 void print_info()\r
149 {\r
150         CASPAR_LOG(info) << L"Copyright (c) 2010 Sveriges Television AB, www.casparcg.com, <info@casparcg.com>";\r
151         CASPAR_LOG(info) << L"Starting CasparCG Video and Graphics Playout Server " << caspar::env::version();\r
152         CASPAR_LOG(info) << L"on " << caspar::get_win_product_name() << L" " << caspar::get_win_sp_version();\r
153         CASPAR_LOG(info) << caspar::get_cpu_info();\r
154         CASPAR_LOG(info) << caspar::get_system_product_name();\r
155         CASPAR_LOG(info) << L"Flash " << caspar::flash::get_version();\r
156         CASPAR_LOG(info) << L"FreeImage " << caspar::image::get_version();\r
157         \r
158         CASPAR_LOG(info) << L"Decklink " << caspar::decklink::get_version();\r
159         auto deck = caspar::decklink::get_device_list();\r
160         std::for_each(deck.begin(), deck.end(), [](const std::wstring& device)\r
161         {\r
162                 CASPAR_LOG(info) << L" - " << device;\r
163         });\r
164                 \r
165         CASPAR_LOG(info) << L"Bluefish " << caspar::bluefish::get_version();\r
166         auto blue = caspar::bluefish::get_device_list();\r
167         std::for_each(blue.begin(), blue.end(), [](const std::wstring& device)\r
168         {\r
169                 CASPAR_LOG(info) << L" - " << device;\r
170         });\r
171         \r
172         CASPAR_LOG(info) << L"FFMPEG-avcodec "  << caspar::ffmpeg::get_avcodec_version();\r
173         CASPAR_LOG(info) << L"FFMPEG-avformat " << caspar::ffmpeg::get_avformat_version();\r
174         CASPAR_LOG(info) << L"FFMPEG-avfilter " << caspar::ffmpeg::get_avfilter_version();\r
175         CASPAR_LOG(info) << L"FFMPEG-avutil "   << caspar::ffmpeg::get_avutil_version();\r
176         CASPAR_LOG(info) << L"FFMPEG-swscale "  << caspar::ffmpeg::get_swscale_version();\r
177 }\r
178 \r
179 LONG WINAPI UserUnhandledExceptionFilter(EXCEPTION_POINTERS* info)\r
180 {\r
181         try\r
182         {\r
183                 CASPAR_LOG(fatal) << L"#######################\n UNHANDLED EXCEPTION: \n" \r
184                         << L"Adress:" << info->ExceptionRecord->ExceptionAddress << L"\n"\r
185                         << L"Code:" << info->ExceptionRecord->ExceptionCode << L"\n"\r
186                         << L"Flag:" << info->ExceptionRecord->ExceptionFlags << L"\n"\r
187                         << L"Info:" << info->ExceptionRecord->ExceptionInformation << L"\n"\r
188                         << L"Continuing execution. \n#######################";\r
189         }\r
190         catch(...){}\r
191 \r
192     return EXCEPTION_EXECUTE_HANDLER;\r
193 }\r
194 \r
195 int main(int argc, wchar_t* argv[])\r
196 {       \r
197         static_assert(sizeof(void*) == 4, "64-bit code generation is not supported.");\r
198         \r
199         SetUnhandledExceptionFilter(UserUnhandledExceptionFilter);\r
200 \r
201         std::wcout << L"Type \"q\" to close application." << std::endl;\r
202         \r
203         // Set debug mode.\r
204         #ifdef _DEBUG\r
205                 _CrtSetDbgFlag ( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF | _CRTDBG_CHECK_ALWAYS_DF );\r
206                 _CrtSetReportMode( _CRT_ERROR, _CRTDBG_MODE_DEBUG );\r
207                 _CrtSetReportMode( _CRT_WARN, _CRTDBG_MODE_DEBUG );\r
208                 _CrtSetReportMode( _CRT_ASSERT, _CRTDBG_MODE_DEBUG );\r
209         #endif\r
210 \r
211         // Increase process priotity.\r
212         SetPriorityClass(GetCurrentProcess(), ABOVE_NORMAL_PRIORITY_CLASS);\r
213 \r
214         // Install structured exception handler.\r
215         caspar::win32_exception::install_handler();\r
216 \r
217         caspar::log::set_log_level(L"debug");\r
218                         \r
219         // Increase time precision. This will increase accuracy of function like Sleep(1) from 10 ms to 1 ms.\r
220         struct inc_prec\r
221         {\r
222                 inc_prec(){timeBeginPeriod(1);}\r
223                 ~inc_prec(){timeEndPeriod(1);}\r
224         } inc_prec;     \r
225 \r
226         // Install unstructured exception handlers into all tbb threads.\r
227         struct tbb_thread_installer : public tbb::task_scheduler_observer\r
228         {\r
229                 tbb_thread_installer(){observe(true);}\r
230                 void on_scheduler_entry(bool is_worker)\r
231                 {\r
232                         //caspar::detail::SetThreadName(GetCurrentThreadId(), "tbb-worker-thread");\r
233                         caspar::win32_exception::install_handler();\r
234                 }\r
235         } tbb_thread_installer;\r
236 \r
237         tbb::task_scheduler_init init;\r
238         \r
239         try \r
240         {\r
241                 {\r
242                         // Configure environment properties from configuration.\r
243                         caspar::env::configure("casparcg.config");\r
244                                 \r
245                 #ifdef _DEBUG\r
246                         if(caspar::env::properties().get("configuration.debugging.remote", false))\r
247                                 MessageBox(nullptr, TEXT("Now is the time to connect for remote debugging..."), TEXT("Debug"), MB_OK | MB_TOPMOST);\r
248                 #endif   \r
249 \r
250                         // Start logging to file.\r
251                         caspar::log::add_file_sink(caspar::env::log_folder());                  \r
252                         std::wcout << L"Logging [info] or higher severity to " << caspar::env::log_folder() << std::endl << std::endl;\r
253                 \r
254                         // Setup console window.\r
255                         setup_console_window();\r
256 \r
257                         // Print environment information.\r
258                         print_info();\r
259                         \r
260                         std::stringstream str;\r
261                         boost::property_tree::xml_writer_settings<char> w(' ', 3);\r
262                         boost::property_tree::write_xml(str, caspar::env::properties(), w);\r
263                         CASPAR_LOG(info) << L"casparcg.config:\n-----------------------------------------\n" << str.str().c_str() << L"-----------------------------------------";\r
264                                 \r
265                         // Create server object which initializes channels, protocols and controllers.\r
266                         caspar::server caspar_server;\r
267                                 \r
268                         // Create a amcp parser for console commands.\r
269                         caspar::protocol::amcp::AMCPProtocolStrategy amcp(caspar_server.get_channels());\r
270 \r
271                         // Create a dummy client which prints amcp responses to console.\r
272                         auto console_client = std::make_shared<caspar::IO::ConsoleClientInfo>();\r
273 \r
274                         boost::thread input_thread([&]\r
275                         {\r
276                                 while(shutdown_event == application_state::running)\r
277                                 {\r
278                                         std::wstring wcmd;\r
279                                         std::getline(std::wcin, wcmd); // TODO: It's blocking...\r
280 \r
281                                         try\r
282                                         {\r
283                                                 if(wcmd == L"exit" || wcmd == L"q")\r
284                                                 {\r
285                                                         shutdown_event = application_state::pause_and_shutdown;\r
286                                                         shutdown_cond.notify_all();\r
287                                                         return;\r
288                                                 }\r
289 \r
290                                                 // This is just dummy code for testing.\r
291                                                 if(wcmd.substr(0, 1) == L"1")\r
292                                                         wcmd = L"LOADBG 1-1 " + wcmd.substr(1, wcmd.length()-1) + L" SLIDE 100 LOOP \r\nPLAY 1-1";\r
293                                                 else if(wcmd.substr(0, 1) == L"2")\r
294                                                         wcmd = L"MIXER 1-0 VIDEO IS_KEY 1";\r
295                                                 else if(wcmd.substr(0, 1) == L"3")\r
296                                                         wcmd = L"CG 1-2 ADD 1 BBTELEFONARE 1";\r
297                                                 else if(wcmd.substr(0, 1) == L"4")\r
298                                                         wcmd = L"PLAY 1-1 DV FILTER yadif=1:-1 LOOP";\r
299                                                 else if(wcmd.substr(0, 1) == L"5")\r
300                                                 {\r
301                                                         auto file = wcmd.substr(2, wcmd.length()-1);\r
302                                                         wcmd = L"PLAY 1-1 " + file + L" LOOP\r\n" \r
303                                                                    L"PLAY 1-2 " + file + L" LOOP\r\n" \r
304                                                                    L"PLAY 1-3 " + file + L" LOOP\r\n"\r
305                                                                    L"PLAY 2-1 " + file + L" LOOP\r\n" \r
306                                                                    L"PLAY 2-2 " + file + L" LOOP\r\n" \r
307                                                                    L"PLAY 2-3 " + file + L" LOOP\r\n";\r
308                                                 }\r
309                                                 else if(wcmd.substr(0, 1) == L"X")\r
310                                                 {\r
311                                                         int num = 0;\r
312                                                         std::wstring file;\r
313                                                         try\r
314                                                         {\r
315                                                                 num = boost::lexical_cast<int>(wcmd.substr(1, 2));\r
316                                                                 file = wcmd.substr(4, wcmd.length()-1);\r
317                                                         }\r
318                                                         catch(...)\r
319                                                         {\r
320                                                                 num = boost::lexical_cast<int>(wcmd.substr(1, 1));\r
321                                                                 file = wcmd.substr(3, wcmd.length()-1);\r
322                                                         }\r
323 \r
324                                                         int n = 0;\r
325                                                         int num2 = num;\r
326                                                         while(num2 > 0)\r
327                                                         {\r
328                                                                 num2 >>= 1;\r
329                                                                 n++;\r
330                                                         }\r
331 \r
332                                                         wcmd = L"MIXER 1 GRID " + boost::lexical_cast<std::wstring>(n);\r
333 \r
334                                                         for(int i = 1; i <= num; ++i)\r
335                                                                 wcmd += L"\r\nPLAY 1-" + boost::lexical_cast<std::wstring>(i) + L" " + file + L" LOOP";// + L" SLIDE 100 LOOP";\r
336                                                 }\r
337 \r
338                                                 wcmd += L"\r\n";\r
339                                                 amcp.Parse(wcmd.c_str(), wcmd.length(), console_client);\r
340                                         }\r
341                                         catch(...)\r
342                                         {\r
343                                                 CASPAR_LOG_CURRENT_EXCEPTION();\r
344                                         }\r
345                                 }\r
346                         });\r
347 \r
348                         boost::unique_lock<boost::mutex> lock(shutdown_mut);\r
349                         while(shutdown_event == application_state::running)                     \r
350                                 shutdown_cond.wait(lock);                               \r
351                 }       \r
352                         \r
353                 Sleep(500); // CAPSAR_LOG is asynchronous. Try to get text in correct order.'\r
354 \r
355                 if(shutdown_event == application_state::pause_and_shutdown)\r
356                         system("pause");        \r
357         }\r
358         catch(boost::property_tree::file_parser_error&)\r
359         {\r
360                 CASPAR_LOG_CURRENT_EXCEPTION();\r
361                 CASPAR_LOG(fatal) << L"Unhandled configuration error in main thread. Please check the configuration file (casparcg.config) for errors.";\r
362                 system("pause");        \r
363         }\r
364         catch(caspar::gl::ogl_exception&)\r
365         {\r
366                 CASPAR_LOG_CURRENT_EXCEPTION();\r
367                 CASPAR_LOG(fatal) << L"Unhandled OpenGL Error in main thread. Please try to update graphics drivers for OpenGL 3.0+ Support.";\r
368         }\r
369         catch(...)\r
370         {\r
371                 CASPAR_LOG_CURRENT_EXCEPTION();\r
372                 CASPAR_LOG(fatal) << L"Unhandled exception in main thread. Please report this error on the CasparCG forums (www.casparcg.com/forum).";\r
373         }       \r
374         \r
375         CASPAR_LOG(info) << "Successfully shutdown CasparCG Server.";\r
376         return 0;\r
377 }