/** * @file src/main.cpp * @brief Main entry point for Sunshine. */ // standard includes #include #include #include #include // local includes #include "globals.h" #include "interprocess.h" #include "logging.h" #include "main.h" #include "version.h" #include "video.h" #include "audio.h" #include "input.h" #include "config.h" #ifdef _WIN32 #include #endif using namespace std::literals; using namespace boost::interprocess; std::map> signal_handlers; void on_signal_forwarder(int sig) { signal_handlers.at(sig)(); } template void on_signal(int sig, FN &&fn) { signal_handlers.emplace(sig, std::forward(fn)); std::signal(sig, on_signal_forwarder); } /** * @brief Main application entry point. * @param argc The number of arguments. * @param argv The arguments. * * EXAMPLES: * ```cpp * main(1, const char* args[] = {"sunshine", nullptr}); * ``` */ int main(int argc, char *argv[]) { task_pool_util::TaskPool::task_id_t force_shutdown = nullptr; #ifdef _WIN32 // Switch default C standard library locale to UTF-8 on Windows 10 1803+ setlocale(LC_ALL, ".UTF-8"); #endif #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wdeprecated-declarations" // Use UTF-8 conversion for the default C++ locale (used by boost::log) std::locale::global(std::locale(std::locale(), new std::codecvt_utf8)); #pragma GCC diagnostic pop mail::man = std::make_shared(); auto log_deinit_guard = logging::init(config::sunshine.min_log_level, config::sunshine.log_file); if (!log_deinit_guard) { BOOST_LOG(error) << "Logging failed to initialize"sv; } // logging can begin at this point // if anything is logged prior to this point, it will appear in stdout, but not in the log viewer in the UI // the version should be printed to the log before anything else BOOST_LOG(info) << PROJECT_NAME << " version: " << PROJECT_VER; #ifdef WIN32 // Modify relevant NVIDIA control panel settings if the system has corresponding gpu if (nvprefs_instance.load()) { // Restore global settings to the undo file left by improper termination of sunshine.exe nvprefs_instance.restore_from_and_delete_undo_file_if_exists(); // Modify application settings for sunshine.exe nvprefs_instance.modify_application_profile(); // Modify global settings, undo file is produced in the process to restore after improper termination nvprefs_instance.modify_global_profile(); // Unload dynamic library to survive driver re-installation nvprefs_instance.unload(); } // Wait as long as possible to terminate Sunshine.exe during logoff/shutdown SetProcessShutdownParameters(0x100, SHUTDOWN_NORETRY); // We must create a hidden window to receive shutdown notifications since we load gdi32.dll std::promise session_monitor_hwnd_promise; auto session_monitor_hwnd_future = session_monitor_hwnd_promise.get_future(); std::promise session_monitor_join_thread_promise; auto session_monitor_join_thread_future = session_monitor_join_thread_promise.get_future(); #endif task_pool.start(1); // Create signal handler after logging has been initialized auto shutdown_event = mail::man->event(mail::shutdown); on_signal(SIGINT, [&force_shutdown, shutdown_event]() { BOOST_LOG(info) << "Interrupt handler called"sv; auto task = []() { BOOST_LOG(fatal) << "10 seconds passed, yet Sunshine's still running: Forcing shutdown"sv; logging::log_flush(); }; force_shutdown = task_pool.pushDelayed(task, 10s).task_id; shutdown_event->raise(true); }); on_signal(SIGTERM, [&force_shutdown, shutdown_event]() { BOOST_LOG(info) << "Terminate handler called"sv; auto task = []() { BOOST_LOG(fatal) << "10 seconds passed, yet Sunshine's still running: Forcing shutdown"sv; logging::log_flush(); }; force_shutdown = task_pool.pushDelayed(task, 10s).task_id; shutdown_event->raise(true); }); // If any of the following fail, we log an error and continue event though sunshine will not function correctly. // This allows access to the UI to fix configuration problems or view the logs. auto platf_deinit_guard = platf::init(); if (!platf_deinit_guard) { BOOST_LOG(error) << "Platform failed to initialize"sv; } auto input_deinit_guard = input::init(); if (video::probe_encoders()) { BOOST_LOG(error) << "Video failed to find working encoder"sv; } //Open managed segment managed_shared_memory segment(open_only, "MySharedMemory"); //An handle from the base address can identify any byte of the shared //memory segment even if it is mapped in different base addresses managed_shared_memory::handle_t handle = 0; //Obtain handle value std::stringstream s; s << argv[1]; s >> handle; //Get buffer local address from handle SharedMemory* memory = (SharedMemory*)segment.get_address_from_handle(handle); auto video_capture = std::thread{[&](){ video::capture(mail::man,video::config_t{ 1920, 1080, 60, 6000, 1, 0, 1, 0, 0 },NULL); }}; auto audio_capture = std::thread{[&](){ audio::capture(mail::man,audio::config_t{ 10,2,3,0 },NULL); }}; auto video_packets = mail::man->queue(mail::video_packets); auto audio_packets = mail::man->queue(mail::audio_packets); auto video_handle = std::thread{[&](){ while (!shutdown_event->peek()) { if(!video_packets->peek()) { std::this_thread::sleep_for(1ms); continue; } auto packet = video_packets->pop(); BOOST_LOG(info) << "Receive video packet size " << packet->data_size() << "\n"; } }}; auto audio_handle = std::thread{[&](){ while (!shutdown_event->peek()) { if(!audio_packets->peek()) { std::this_thread::sleep_for(1ms); continue; } auto packet = audio_packets->pop(); BOOST_LOG(info) << "Receive audio packet size " << packet->second.size() << "\n"; } }}; while (!shutdown_event->peek()) std::this_thread::sleep_for(1s); video_handle.join(); audio_handle.join(); audio_capture.join(); video_capture.join(); task_pool.stop(); task_pool.join(); #ifdef WIN32 // Restore global NVIDIA control panel settings if (nvprefs_instance.owning_undo_file() && nvprefs_instance.load()) { nvprefs_instance.restore_global_profile(); nvprefs_instance.unload(); } #endif return 0; }