X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=shell%2Fmain.cpp;h=2eac1b8b646fc52a9d0aff964189d3bfd30128f1;hb=181d938c6b2c4fd014bc420960e51d38c399b1e4;hp=2d462a463cafacda19c02249c8e08b9e57ae807a;hpb=c3a76b811579dd3a52227e170903da821c04ae9e;p=casparcg diff --git a/shell/main.cpp b/shell/main.cpp index 2d462a463..2eac1b8b6 100644 --- a/shell/main.cpp +++ b/shell/main.cpp @@ -25,112 +25,64 @@ #include "stdafx.h" -#ifdef _DEBUG +#include +#include + +#if defined _DEBUG && defined _MSC_VER #define _CRTDBG_MAP_ALLOC #include #include #else - #include + // Reenable when tbb gets official support for vc14 + //#include #endif -#include "resource.h" - #include "server.h" - -#include -#include -#include -#include +#include "platform_specific.h" +#include "included_modules.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 -using namespace caspar; - -// NOTE: This is needed in order to make CComObject work since this is not a real ATL project. -CComModule _AtlModule; -extern __declspec(selectany) CAtlModule* _pAtlModule = &_AtlModule; +#include +#include -void change_icon( const HICON hNewIcon ) -{ - auto hMod = ::LoadLibrary(L"Kernel32.dll"); - typedef DWORD(__stdcall *SCI)(HICON); - auto pfnSetConsoleIcon = reinterpret_cast(::GetProcAddress(hMod, "SetConsoleIcon")); - pfnSetConsoleIcon(hNewIcon); - ::FreeLibrary(hMod); -} +#include +#include +using namespace caspar; + void setup_global_locale() { boost::locale::generator gen; gen.categories(boost::locale::codepage_facet); std::locale::global(gen("")); -} - -void setup_console_window() -{ - auto hOut = GetStdHandle(STD_OUTPUT_HANDLE); - - // Disable close button in console to avoid shutdown without cleanup. - EnableMenuItem(GetSystemMenu(GetConsoleWindow(), FALSE), SC_CLOSE , MF_GRAYED); - DrawMenuBar(GetConsoleWindow()); - //SetConsoleCtrlHandler(HandlerRoutine, true); - // Configure console size and position. - auto coord = GetLargestConsoleWindowSize(hOut); - coord.X /= 2; - - SetConsoleScreenBufferSize(hOut, coord); - - SMALL_RECT DisplayArea = {0, 0, 0, 0}; - DisplayArea.Right = coord.X-1; - DisplayArea.Bottom = (coord.Y-1)/2; - SetConsoleWindowInfo(hOut, TRUE, &DisplayArea); - - change_icon(::LoadIcon(GetModuleHandle(0), MAKEINTRESOURCE(101))); - - // Set console title. - std::wstringstream str; - str << "CasparCG Server " << env::version() << L" x64 "; -#ifdef COMPILE_RELEASE - str << " Release"; -#elif COMPILE_PROFILE - str << " Profile"; -#elif COMPILE_DEVELOP - str << " Develop"; -#elif COMPILE_DEBUG - str << " Debug"; -#endif - SetConsoleTitle(str.str().c_str()); + // sscanf is used in for example FFmpeg where we want decimals to be parsed as . + std::setlocale(LC_ALL, "C"); } void print_info() @@ -142,54 +94,26 @@ void print_info() CASPAR_LOG(info) << L"http://www.casparcg.com/"; CASPAR_LOG(info) << L"############################################################################"; CASPAR_LOG(info) << L"Starting CasparCG Video and Graphics Playout Server " << env::version(); - CASPAR_LOG(info) << L"on " << win_product_name() << L" " << win_sp_version(); + CASPAR_LOG(info) << L"on " << os_description(); CASPAR_LOG(info) << cpu_info(); CASPAR_LOG(info) << system_product_name(); - - CASPAR_LOG(info) << L"Decklink " << decklink::version(); - BOOST_FOREACH(auto device, decklink::device_list()) - CASPAR_LOG(info) << L" - " << device; - - CASPAR_LOG(info) << L"Bluefish " << bluefish::version(); - BOOST_FOREACH(auto device, bluefish::device_list()) - CASPAR_LOG(info) << L" - " << device; - - CASPAR_LOG(info) << L"Flash " << flash::version(); - CASPAR_LOG(info) << L"FreeImage " << image::version(); - CASPAR_LOG(info) << L"FFMPEG-avcodec " << ffmpeg::avcodec_version(); - CASPAR_LOG(info) << L"FFMPEG-avformat " << ffmpeg::avformat_version(); - CASPAR_LOG(info) << L"FFMPEG-avfilter " << ffmpeg::avfilter_version(); - CASPAR_LOG(info) << L"FFMPEG-avutil " << ffmpeg::avutil_version(); - CASPAR_LOG(info) << L"FFMPEG-swscale " << ffmpeg::swscale_version(); } -LONG WINAPI UserUnhandledExceptionFilter(EXCEPTION_POINTERS* info) + +void print_system_info(const spl::shared_ptr& repo) { - try - { - CASPAR_LOG(fatal) << L"#######################\n UNHANDLED EXCEPTION: \n" - << L"Adress:" << info->ExceptionRecord->ExceptionAddress << L"\n" - << L"Code:" << info->ExceptionRecord->ExceptionCode << L"\n" - << L"Flag:" << info->ExceptionRecord->ExceptionFlags << L"\n" - << L"Info:" << info->ExceptionRecord->ExceptionInformation << L"\n" - << L"Continuing execution. \n#######################"; - - CASPAR_LOG_CALL_STACK(); - } - catch(...){} + boost::property_tree::wptree info; + repo->fill_information(info); - return EXCEPTION_CONTINUE_EXECUTION; + for (auto& elem : info.get_child(L"system")) + log::print_child(boost::log::trivial::info, L"", elem.first, elem.second); } -void do_run(server& caspar_server, boost::promise& shutdown_server_now) +void do_run( + std::weak_ptr> amcp, + std::promise& shutdown_server_now, + tbb::atomic& should_wait_for_keypress) { - // Create a dummy client which prints amcp responses to console. - auto console_client = spl::make_shared(); - - // Create a amcp parser for console commands. - //protocol::amcp::AMCPProtocolStrategy amcp(caspar_server.channels()); - auto amcp = spl::make_shared>(L"\r\n", spl::make_shared(spl::make_shared(caspar_server.channels(), caspar_server.get_thumbnail_generator(), shutdown_server_now)))->create(console_client); - std::wstring wcmd; while(true) { @@ -199,7 +123,9 @@ void do_run(server& caspar_server, boost::promise& shutdown_server_now) if(boost::iequals(wcmd, L"EXIT") || boost::iequals(wcmd, L"Q") || boost::iequals(wcmd, L"QUIT") || boost::iequals(wcmd, L"BYE")) { - shutdown_server_now.set_value(true); //true to wait for keypress + CASPAR_LOG(info) << L"Received message from Console: " << wcmd << L"\\r\\n"; + should_wait_for_keypress = true; + shutdown_server_now.set_value(false); //false to not restart break; } @@ -277,33 +203,62 @@ void do_run(server& caspar_server, boost::promise& shutdown_server_now) } wcmd += L"\r\n"; - amcp->parse(wcmd); + auto strong = amcp.lock(); + if (strong) + strong->parse(wcmd); + else + break; } }; -bool run() +bool run(const std::wstring& config_file_name, tbb::atomic& should_wait_for_keypress) { - boost::promise shutdown_server_now; - boost::unique_future shutdown_server = shutdown_server_now.get_future(); + std::promise shutdown_server_now; + std::future shutdown_server = shutdown_server_now.get_future(); + + print_info(); // Create server object which initializes channels, protocols and controllers. - server caspar_server(shutdown_server_now); + std::unique_ptr caspar_server(new server(shutdown_server_now)); + + // For example CEF resets the global locale, so this is to reset it back to "our" preference. + setup_global_locale(); + + // Print environment information. + print_system_info(caspar_server->get_system_info_provider_repo()); + + std::wstringstream str; + boost::property_tree::xml_writer_settings w(' ', 3); + boost::property_tree::write_xml(str, env::properties(), w); + CASPAR_LOG(info) << config_file_name << L":\n-----------------------------------------\n" << str.str() << L"-----------------------------------------"; - //auto console_obs = reactive::make_observer([](const monitor::event& e) - //{ - // std::stringstream str; - // str << e; - // CASPAR_LOG(trace) << str.str().c_str(); - //}); + { + CASPAR_SCOPED_CONTEXT_MSG(config_file_name + L": ") + caspar_server->start(); + } - //caspar_server.subscribe(console_obs); + // Create a dummy client which prints amcp responses to console. + auto console_client = spl::make_shared(); + // Create a amcp parser for console commands. + std::shared_ptr> amcp = spl::make_shared>( + L"\r\n", + spl::make_shared( + spl::make_shared( + L"Console", + caspar_server->get_amcp_command_repository())))->create(console_client); + std::weak_ptr> weak_amcp = amcp; // Use separate thread for the blocking console input, will be terminated // anyway when the main thread terminates. - boost::thread stdin_thread(std::bind(do_run, std::ref(caspar_server), std::ref(shutdown_server_now))); //compiler didn't like lambda here... + boost::thread stdin_thread(std::bind(do_run, weak_amcp, std::ref(shutdown_server_now), std::ref(should_wait_for_keypress))); //compiler didn't like lambda here... stdin_thread.detach(); - return shutdown_server.get(); + bool should_restart = shutdown_server.get(); + amcp.reset(); + + while (weak_amcp.lock()); + + return should_restart; } void on_abort(int) @@ -311,103 +266,103 @@ void on_abort(int) CASPAR_THROW_EXCEPTION(invalid_operation() << msg_info("abort called")); } -int main(int argc, wchar_t* argv[]) -{ +int main(int argc, char** argv) +{ + if (intercept_command_line_args(argc, argv)) + return 0; + int return_code = 0; - SetUnhandledExceptionFilter(UserUnhandledExceptionFilter); - signal(SIGABRT, on_abort); + setup_prerequisites(); + //std::signal(SIGABRT, on_abort); setup_global_locale(); std::wcout << L"Type \"q\" to close application." << std::endl; // Set debug mode. - #ifdef _DEBUG - HANDLE hLogFile; - hLogFile = CreateFile(L"crt_log.txt", GENERIC_WRITE, FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); - std::shared_ptr crt_log(nullptr, [](HANDLE h){::CloseHandle(h);}); - - _CrtSetDbgFlag (_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF); - _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE); - _CrtSetReportFile(_CRT_WARN, hLogFile); - _CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_FILE); - _CrtSetReportFile(_CRT_ERROR, hLogFile); - _CrtSetReportMode(_CRT_ASSERT, _CRTDBG_MODE_FILE); - _CrtSetReportFile(_CRT_ASSERT, hLogFile); - #endif + auto debugging_environment = setup_debugging_environment(); // Increase process priotity. - SetPriorityClass(GetCurrentProcess(), ABOVE_NORMAL_PRIORITY_CLASS); + increase_process_priority(); - // Install structured exception handler. - win32_exception::install_handler(); - - // Increase time precision. This will increase accuracy of function like Sleep(1) from 10 ms to 1 ms. - struct inc_prec - { - inc_prec(){timeBeginPeriod(1);} - ~inc_prec(){timeEndPeriod(1);} - } inc_prec; + // Install general protection fault handler. + ensure_gpf_handler_installed_for_thread("main thread"); - // Install SEH into all tbb threads. + // Install GPF handler into all tbb threads. struct tbb_thread_installer : public tbb::task_scheduler_observer { tbb_thread_installer(){observe(true);} - void on_scheduler_entry(bool is_worker) + void on_scheduler_entry(bool is_worker) override { - //detail::SetThreadName(GetCurrentThreadId(), "tbb-worker-thread"); - win32_exception::install_handler(); + ensure_gpf_handler_installed_for_thread("tbb-worker-thread"); } } tbb_thread_installer; tbb::task_scheduler_init init; + std::wstring config_file_name(L"casparcg.config"); try { // Configure environment properties from configuration. - env::configure(L"casparcg.config"); - - log::set_log_level(env::properties().get(L"configuration.log-level", L"debug")); + if (argc >= 2) + config_file_name = caspar::u16(argv[1]); + + env::configure(config_file_name); + + log::set_log_level(env::properties().get(L"configuration.log-level", L"info")); + auto log_categories_str = env::properties().get(L"configuration.log-categories", L"communication"); + std::set log_categories; + boost::split(log_categories, log_categories_str, boost::is_any_of(L", ")); + for (auto& log_category : { L"calltrace", L"communication" }) + log::set_log_category(log_category, log_categories.find(log_category) != log_categories.end()); - #ifdef _DEBUG - if(env::properties().get(L"configuration.debugging.remote", false)) - MessageBox(nullptr, TEXT("Now is the time to connect for remote debugging..."), TEXT("Debug"), MB_OK | MB_TOPMOST); - #endif + if (env::properties().get(L"configuration.debugging.remote", false)) + wait_for_remote_debugging(); // Start logging to file. - log::add_file_sink(env::log_folder()); + log::add_file_sink(env::log_folder() + L"caspar", caspar::log::category != caspar::log::log_category::calltrace); + log::add_file_sink(env::log_folder() + L"calltrace", caspar::log::category == caspar::log::log_category::calltrace); std::wcout << L"Logging [info] or higher severity to " << env::log_folder() << std::endl << std::endl; // Setup console window. setup_console_window(); - // Print environment information. - print_info(); - - std::wstringstream str; - boost::property_tree::xml_writer_settings w(' ', 3); - boost::property_tree::write_xml(str, env::properties(), w); - CASPAR_LOG(info) << L"casparcg.config:\n-----------------------------------------\n" << str.str().c_str() << L"-----------------------------------------"; - - return_code = run() ? 5 : 0; + tbb::atomic should_wait_for_keypress; + should_wait_for_keypress = false; + auto should_restart = run(config_file_name, should_wait_for_keypress); + return_code = should_restart ? 5 : 0; + + for (auto& thread : get_thread_infos()) + { + if (thread->name != "main thread" && thread->name != "tbb-worker-thread") + CASPAR_LOG(warning) << L"Thread left running: " << thread->name << L" (" << thread->native_id << L")"; + } - Sleep(500); CASPAR_LOG(info) << "Successfully shutdown CasparCG Server."; + + if (should_wait_for_keypress) + wait_for_keypress(); } - catch(boost::property_tree::file_parser_error&) + catch(const boost::property_tree::file_parser_error& e) { CASPAR_LOG_CURRENT_EXCEPTION(); - CASPAR_LOG(fatal) << L"Unhandled configuration error in main thread. Please check the configuration file (casparcg.config) for errors."; - system("pause"); + CASPAR_LOG(fatal) << "At " << u8(config_file_name) << ":" << e.line() << ": " << e.message() << ". Please check the configuration file (" << u8(config_file_name) << ") for errors."; + wait_for_keypress(); + } + catch (const user_error& e) + { + CASPAR_LOG_CURRENT_EXCEPTION_AT_LEVEL(debug); + CASPAR_LOG(fatal) << get_message_and_context(e) << " Please check the configuration file (" << u8(config_file_name) << ") for errors. Turn on log level debug for stacktrace."; + wait_for_keypress(); } catch(...) { CASPAR_LOG_CURRENT_EXCEPTION(); CASPAR_LOG(fatal) << L"Unhandled exception in main thread. Please report this error on the CasparCG forums (www.casparcg.com/forum)."; - Sleep(1000); + boost::this_thread::sleep_for(boost::chrono::milliseconds(1000)); std::wcout << L"\n\nCasparCG will automatically shutdown. See the log file located at the configured log-file folder for more information.\n\n"; - Sleep(4000); - } - + boost::this_thread::sleep_for(boost::chrono::milliseconds(4000)); + } + return return_code; -} \ No newline at end of file +}