diff --git a/include/CaptureDeviceSpecific/ISNitro/usb_is_nitro_acquisition_general.hpp b/include/CaptureDeviceSpecific/ISNitro/usb_is_nitro_acquisition_general.hpp index 86faaba..f675c8a 100644 --- a/include/CaptureDeviceSpecific/ISNitro/usb_is_nitro_acquisition_general.hpp +++ b/include/CaptureDeviceSpecific/ISNitro/usb_is_nitro_acquisition_general.hpp @@ -6,7 +6,7 @@ #include "display_structs.hpp" uint64_t _is_nitro_get_video_in_size(CaptureScreensType capture_type); -int set_acquisition_mode(is_nitro_device_handlers* handlers, CaptureScreensType capture_type, const is_nitro_usb_device* usb_device_desc); +int set_acquisition_mode(is_nitro_device_handlers* handlers, CaptureScreensType capture_type, CaptureSpeedsType capture_speed, const is_nitro_usb_device* usb_device_desc); int EndAcquisition(is_nitro_device_handlers* handlers, bool do_drain_frames, int start_frames, CaptureScreensType capture_type, const is_nitro_usb_device* usb_device_desc); int is_nitro_read_frame_and_output(CaptureData* capture_data, int& inner_curr_in, CaptureScreensType curr_capture_type, std::chrono::time_point &clock_start); diff --git a/include/Menus/ISNitroMenu.hpp b/include/Menus/ISNitroMenu.hpp index 6484fc5..025728c 100644 --- a/include/Menus/ISNitroMenu.hpp +++ b/include/Menus/ISNitroMenu.hpp @@ -16,6 +16,8 @@ enum ISNitroMenuOutAction{ ISN_MENU_TYPE_DEC, ISN_MENU_TYPE_INC, ISN_MENU_RESET, + ISN_MENU_SPEED_INC, + ISN_MENU_SPEED_DEC, }; class ISNitroMenu : public OptionSelectionMenu { diff --git a/include/capture_structs.hpp b/include/capture_structs.hpp index 54860c1..4c561b6 100755 --- a/include/capture_structs.hpp +++ b/include/capture_structs.hpp @@ -20,6 +20,7 @@ enum CaptureConnectionType { CAPTURE_CONN_FTD3, CAPTURE_CONN_USB, CAPTURE_CONN_FTD2, CAPTURE_CONN_IS_NITRO }; enum CaptureScreensType { CAPTURE_SCREENS_BOTH, CAPTURE_SCREENS_TOP, CAPTURE_SCREENS_BOTTOM, CAPTURE_SCREENS_ENUM_END }; +enum CaptureSpeedsType { CAPTURE_SPEEDS_FULL, CAPTURE_SPEEDS_HALF, CAPTURE_SPEEDS_THIRD, CAPTURE_SPEEDS_QUARTER, CAPTURE_SPEEDS_ENUM_END }; #pragma pack(push, 1) @@ -138,6 +139,7 @@ struct CaptureStatus { volatile bool reset_hardware = false; bool enabled_3d = false; CaptureScreensType capture_type; + CaptureSpeedsType capture_speed; ConsumerMutex video_wait; ConsumerMutex audio_wait; }; diff --git a/include/frontend.hpp b/include/frontend.hpp index cbe58a0..f78725b 100755 --- a/include/frontend.hpp +++ b/include/frontend.hpp @@ -224,6 +224,7 @@ private: void padding_change(); void game_crop_enable_change(); void is_nitro_capture_type_change(bool positive); + void is_nitro_capture_speed_change(bool positive); void is_nitro_capture_reset_hw(); void crop_value_change(int new_crop_value, bool do_print_notification = true, bool do_cycle = true); void par_value_change(int new_par_value, bool is_top); diff --git a/include/utils.hpp b/include/utils.hpp index a0c881d..ad7ed55 100755 --- a/include/utils.hpp +++ b/include/utils.hpp @@ -48,12 +48,14 @@ public: bool timed_lock(); bool try_lock(); void unlock(); + void update_time_multiplier(float time_multiplier); private: std::mutex access_mutex; std::condition_variable_any condition; - const std::chrono::durationmax_timed_wait = std::chrono::duration(1.0 / 30); + const float base_time_fps = 30; int count = 0; + float time_multiplier = 1.0; }; #endif diff --git a/source/CaptureDeviceSpecific/ISNitro/usb_is_nitro_acquisition.cpp b/source/CaptureDeviceSpecific/ISNitro/usb_is_nitro_acquisition.cpp index d56aac0..1eeabf0 100644 --- a/source/CaptureDeviceSpecific/ISNitro/usb_is_nitro_acquisition.cpp +++ b/source/CaptureDeviceSpecific/ISNitro/usb_is_nitro_acquisition.cpp @@ -137,13 +137,33 @@ uint64_t usb_is_nitro_get_video_in_size(CaptureData* capture_data) { return _is_nitro_get_video_in_size(capture_data->status.capture_type); } -int set_acquisition_mode(is_nitro_device_handlers* handlers, CaptureScreensType capture_type, const is_nitro_usb_device* usb_device_desc) { +int set_acquisition_mode(is_nitro_device_handlers* handlers, CaptureScreensType capture_type, CaptureSpeedsType capture_speed, const is_nitro_usb_device* usb_device_desc) { is_nitro_forward_config_values_screens capture_mode_flag = IS_NITRO_FORWARD_CONFIG_MODE_BOTH; - if(capture_type == CAPTURE_SCREENS_TOP) - capture_mode_flag = IS_NITRO_FORWARD_CONFIG_MODE_TOP; - if(capture_type == CAPTURE_SCREENS_BOTTOM) - capture_mode_flag = IS_NITRO_FORWARD_CONFIG_MODE_BOTTOM; - return UpdateFrameForwardConfig(handlers, IS_NITRO_FORWARD_CONFIG_COLOR_RGB24, capture_mode_flag, IS_NITRO_FORWARD_CONFIG_RATE_FULL, usb_device_desc); + switch(capture_type) { + case CAPTURE_SCREENS_TOP: + capture_mode_flag = IS_NITRO_FORWARD_CONFIG_MODE_TOP; + break; + case CAPTURE_SCREENS_BOTTOM: + capture_mode_flag = IS_NITRO_FORWARD_CONFIG_MODE_BOTTOM; + break; + default: + break; + } + is_nitro_forward_config_values_rate capture_rate_flag = IS_NITRO_FORWARD_CONFIG_RATE_FULL; + switch(capture_speed) { + case CAPTURE_SPEEDS_HALF: + capture_rate_flag = IS_NITRO_FORWARD_CONFIG_RATE_HALF; + break; + case CAPTURE_SPEEDS_THIRD: + capture_rate_flag = IS_NITRO_FORWARD_CONFIG_RATE_THIRD; + break; + case CAPTURE_SPEEDS_QUARTER: + capture_rate_flag = IS_NITRO_FORWARD_CONFIG_RATE_QUARTER; + break; + default: + break; + } + return UpdateFrameForwardConfig(handlers, IS_NITRO_FORWARD_CONFIG_COLOR_RGB24, capture_mode_flag, capture_rate_flag, usb_device_desc); } int EndAcquisition(is_nitro_device_handlers* handlers, bool do_drain_frames, int start_frames, CaptureScreensType capture_type, const is_nitro_usb_device* usb_device_desc) { diff --git a/source/CaptureDeviceSpecific/ISNitro/usb_is_nitro_acquisition_capture.cpp b/source/CaptureDeviceSpecific/ISNitro/usb_is_nitro_acquisition_capture.cpp index 5950af7..869361c 100644 --- a/source/CaptureDeviceSpecific/ISNitro/usb_is_nitro_acquisition_capture.cpp +++ b/source/CaptureDeviceSpecific/ISNitro/usb_is_nitro_acquisition_capture.cpp @@ -12,14 +12,14 @@ #define RESET_TIMEOUT 4.0 #define SLEEP_CHECKS_TIME_MS 20 -static int StartAcquisitionCapture(is_nitro_device_handlers* handlers, CaptureScreensType capture_type, bool* is_acquisition_off, const is_nitro_usb_device* usb_device_desc) { +static int StartAcquisitionCapture(is_nitro_device_handlers* handlers, CaptureScreensType capture_type, CaptureSpeedsType capture_speed, bool* is_acquisition_off, const is_nitro_usb_device* usb_device_desc) { int ret = 0; ret = ReadLidState(handlers, is_acquisition_off, usb_device_desc); if(ret < 0) return ret; if(*is_acquisition_off) return LIBUSB_SUCCESS; - ret = set_acquisition_mode(handlers, capture_type, usb_device_desc); + ret = set_acquisition_mode(handlers, capture_type, capture_speed, usb_device_desc); if(ret < 0) return ret; ret = SetForwardFramePermanent(handlers, usb_device_desc); @@ -31,11 +31,12 @@ static int StartAcquisitionCapture(is_nitro_device_handlers* handlers, CaptureSc return StartUsbCaptureDma(handlers, usb_device_desc); } -static int LidReopenCaptureCheck(CaptureData* capture_data, CaptureScreensType &curr_capture_type, bool* is_acquisition_off) { +static int LidReopenCaptureCheck(CaptureData* capture_data, CaptureScreensType &curr_capture_type, CaptureSpeedsType &curr_capture_speed, bool* is_acquisition_off) { is_nitro_device_handlers* handlers = (is_nitro_device_handlers*)capture_data->handle; const is_nitro_usb_device* usb_device_desc = (const is_nitro_usb_device*)capture_data->status.device.descriptor; curr_capture_type = capture_data->status.capture_type; - int ret = StartAcquisitionCapture(handlers, curr_capture_type, is_acquisition_off, usb_device_desc); + curr_capture_speed = capture_data->status.capture_speed; + int ret = StartAcquisitionCapture(handlers, curr_capture_type, curr_capture_speed, is_acquisition_off, usb_device_desc); if(ret < 0) return ret; if(!(*is_acquisition_off)) @@ -43,7 +44,7 @@ static int LidReopenCaptureCheck(CaptureData* capture_data, CaptureScreensType & return ret; } -static int CaptureResetHardware(CaptureData* capture_data, CaptureScreensType &curr_capture_type, bool *is_acquisition_off, std::chrono::time_point &clock_last_reset) { +static int CaptureResetHardware(CaptureData* capture_data, CaptureScreensType &curr_capture_type, CaptureSpeedsType &curr_capture_speed, bool *is_acquisition_off, std::chrono::time_point &clock_last_reset) { is_nitro_device_handlers* handlers = (is_nitro_device_handlers*)capture_data->handle; const is_nitro_usb_device* usb_device_desc = (const is_nitro_usb_device*)capture_data->status.device.descriptor; bool reset_hardware = capture_data->status.reset_hardware; @@ -70,7 +71,8 @@ static int CaptureResetHardware(CaptureData* capture_data, CaptureScreensType &c return ret; if(!(*is_acquisition_off)) { curr_capture_type = capture_data->status.capture_type; - ret = StartAcquisitionCapture(handlers, curr_capture_type, is_acquisition_off, usb_device_desc); + curr_capture_speed = capture_data->status.capture_speed; + ret = StartAcquisitionCapture(handlers, curr_capture_type, curr_capture_speed, is_acquisition_off, usb_device_desc); } if(ret < 0) return ret; @@ -91,7 +93,8 @@ void is_nitro_acquisition_capture_main_loop(CaptureData* capture_data) { const is_nitro_usb_device* usb_device_desc = (const is_nitro_usb_device*)capture_data->status.device.descriptor; bool is_acquisition_off = true; CaptureScreensType curr_capture_type = capture_data->status.capture_type; - int ret = StartAcquisitionCapture(handlers, curr_capture_type, &is_acquisition_off, usb_device_desc); + CaptureSpeedsType curr_capture_speed = capture_data->status.capture_speed; + int ret = StartAcquisitionCapture(handlers, curr_capture_type, curr_capture_speed, &is_acquisition_off, usb_device_desc); if(ret < 0) { capture_error_print(true, capture_data, "Capture Start: Failed"); return; @@ -101,14 +104,14 @@ void is_nitro_acquisition_capture_main_loop(CaptureData* capture_data) { auto clock_last_reset = std::chrono::high_resolution_clock::now(); while(capture_data->status.connected && capture_data->status.running) { - ret = CaptureResetHardware(capture_data, curr_capture_type, &is_acquisition_off, clock_last_reset); + ret = CaptureResetHardware(capture_data, curr_capture_type, curr_capture_speed, &is_acquisition_off, clock_last_reset); if(ret < 0) { capture_error_print(true, capture_data, "Hardware reset: Failed"); return; } if(is_acquisition_off) { default_sleep(SLEEP_CHECKS_TIME_MS); - ret = LidReopenCaptureCheck(capture_data, curr_capture_type, &is_acquisition_off); + ret = LidReopenCaptureCheck(capture_data, curr_capture_type, curr_capture_speed, &is_acquisition_off); if(ret < 0) { capture_error_print(true, capture_data, "Lid Reopen: Failed"); return; @@ -127,14 +130,15 @@ void is_nitro_acquisition_capture_main_loop(CaptureData* capture_data) { continue; } - if(curr_capture_type != capture_data->status.capture_type) { + if((curr_capture_type != capture_data->status.capture_type) || (curr_capture_speed != capture_data->status.capture_speed)) { ret = EndAcquisition(handlers, true, 0, curr_capture_type, usb_device_desc); if(ret < 0) { capture_error_print(true, capture_data, "Capture End: Failed"); return; } curr_capture_type = capture_data->status.capture_type; - ret = StartAcquisitionCapture(handlers, curr_capture_type, &is_acquisition_off, usb_device_desc); + curr_capture_speed = capture_data->status.capture_speed; + ret = StartAcquisitionCapture(handlers, curr_capture_type, curr_capture_speed, &is_acquisition_off, usb_device_desc); if(ret < 0) { capture_error_print(true, capture_data, "Capture Restart: Failed"); return; diff --git a/source/CaptureDeviceSpecific/ISNitro/usb_is_nitro_acquisition_emulator.cpp b/source/CaptureDeviceSpecific/ISNitro/usb_is_nitro_acquisition_emulator.cpp index c0bcf59..fadacb9 100644 --- a/source/CaptureDeviceSpecific/ISNitro/usb_is_nitro_acquisition_emulator.cpp +++ b/source/CaptureDeviceSpecific/ISNitro/usb_is_nitro_acquisition_emulator.cpp @@ -43,12 +43,12 @@ static int drain_frames(is_nitro_device_handlers* handlers, int num_frames, int return LIBUSB_SUCCESS; } -static int StartAcquisitionEmulator(is_nitro_device_handlers* handlers, uint16_t &out_frame_count, float &single_frame_time, CaptureScreensType capture_type, const is_nitro_usb_device* usb_device_desc) { +static int StartAcquisitionEmulator(is_nitro_device_handlers* handlers, uint16_t &out_frame_count, float &single_frame_time, CaptureScreensType capture_type, CaptureSpeedsType capture_speed, const is_nitro_usb_device* usb_device_desc) { int ret = 0; ret = DisableLca2(handlers, usb_device_desc); if(ret < 0) return ret; - ret = set_acquisition_mode(handlers, capture_type, usb_device_desc); + ret = set_acquisition_mode(handlers, capture_type, capture_speed, usb_device_desc); if(ret < 0) return ret; ret = SetForwardFrameCount(handlers, FRAME_BUFFER_SIZE, usb_device_desc); @@ -125,35 +125,64 @@ static int StartAcquisitionEmulator(is_nitro_device_handlers* handlers, uint16_t return ret; } -static bool do_sleep(float single_frame_time, std::chrono::time_point clock_last_reset, int curr_frame_counter, int last_frame_counter, float* out_time) { +static bool do_sleep(float single_frame_time, std::chrono::time_point clock_last_reset, CaptureScreensType capture_type, CaptureSpeedsType capture_speed, int curr_frame_counter, int last_frame_counter, float* out_time) { auto curr_time = std::chrono::high_resolution_clock::now(); std::chrono::duration diff = curr_time - clock_last_reset; float expected_time = single_frame_time * curr_frame_counter; + // Frames aren't ready asap. It takes a while for them to be, in slower modes... + int adding_single_frame_time_divisor = 0; + switch(capture_speed) { + case CAPTURE_SPEEDS_HALF: + adding_single_frame_time_divisor = 3; + break; + case CAPTURE_SPEEDS_THIRD: + adding_single_frame_time_divisor = 4; + break; + case CAPTURE_SPEEDS_QUARTER: + adding_single_frame_time_divisor = 6; + break; + } + if((capture_type == CAPTURE_SCREENS_TOP) || (capture_type == CAPTURE_SCREENS_BOTTOM)) + adding_single_frame_time_divisor *= 2; + if(adding_single_frame_time_divisor > 0) + expected_time += (single_frame_time * (adding_single_frame_time_divisor - 1)) / adding_single_frame_time_divisor; float low_single_frame_time = 1.0 / ((int)((1.0 / single_frame_time) + 1)); float low_expected_time = low_single_frame_time * curr_frame_counter; + float divisor = 4; // If the current time is too low, sleep a bit to make sure we don't overrun the framerate counter // Don't do it regardless of the situation, and only in small increments... // Otherwise there is the risk of sleeping too much - bool result = (diff.count() < expected_time) && ((expected_time - diff.count()) > (single_frame_time / 4)) && (!(last_frame_counter & (FRAME_BUFFER_SIZE - 1))); - *out_time = (expected_time - diff.count()) / 4; + bool result = (diff.count() < expected_time) && ((expected_time - diff.count()) > (single_frame_time / divisor)); + *out_time = (expected_time - diff.count()) / divisor; if(last_frame_counter & (FRAME_BUFFER_SIZE - 1)) { - result = (diff.count() < low_expected_time) && ((low_expected_time - diff.count()) > (low_single_frame_time / 4)); - *out_time = (low_expected_time - diff.count()) / 4; + result = (diff.count() < low_expected_time) && ((low_expected_time - diff.count()) > (low_single_frame_time / divisor)); + *out_time = (low_expected_time - diff.count()) / divisor; } return result; } -static void frame_wait(float single_frame_time, std::chrono::time_point clock_last_reset, int curr_frame_counter, int last_frame_counter) { - if(curr_frame_counter == 0) - return; +static void frame_wait(float single_frame_time, std::chrono::time_point clock_last_reset, CaptureScreensType capture_type, CaptureSpeedsType capture_speed, int curr_frame_counter, int last_frame_counter) { float sleep_time = 0; - while(do_sleep(single_frame_time, clock_last_reset, curr_frame_counter, last_frame_counter, &sleep_time)) + while(do_sleep(single_frame_time, clock_last_reset, capture_type, capture_speed, curr_frame_counter, last_frame_counter, &sleep_time)) default_sleep(sleep_time); } -static int reset_acquisition_frames(is_nitro_device_handlers* handlers, uint16_t &curr_frame_counter, uint16_t &last_frame_counter, float &single_frame_time, std::chrono::time_point &clock_last_reset, CaptureScreensType &curr_capture_type, CaptureScreensType wanted_capture_type, int multiplier, const is_nitro_usb_device* usb_device_desc) { +static int reset_acquisition_frames(is_nitro_device_handlers* handlers, uint16_t &curr_frame_counter, uint16_t &last_frame_counter, float &single_frame_time, std::chrono::time_point &clock_last_reset, CaptureScreensType &curr_capture_type, CaptureScreensType wanted_capture_type, CaptureSpeedsType &curr_capture_speed, CaptureSpeedsType wanted_capture_speed, const is_nitro_usb_device* usb_device_desc) { curr_frame_counter += 1; + int multiplier = 1; + switch(curr_capture_speed) { + case CAPTURE_SPEEDS_HALF: + multiplier = 2; + break; + case CAPTURE_SPEEDS_THIRD: + multiplier = 3; + break; + case CAPTURE_SPEEDS_QUARTER: + multiplier = 4; + break; + } + if(curr_frame_counter == FRAME_BUFFER_SIZE) { int ret = StopUsbCaptureDma(handlers, usb_device_desc); if(ret < 0) @@ -161,9 +190,10 @@ static int reset_acquisition_frames(is_nitro_device_handlers* handlers, uint16_t // If the user requests a mode change, accomodate them. // Though it may lag for a bit... - if(wanted_capture_type != curr_capture_type) { + if((wanted_capture_type != curr_capture_type) || (wanted_capture_speed != curr_capture_speed)) { curr_capture_type = wanted_capture_type; - ret = set_acquisition_mode(handlers, curr_capture_type, usb_device_desc); + curr_capture_speed = wanted_capture_speed; + ret = set_acquisition_mode(handlers, curr_capture_type, curr_capture_speed, usb_device_desc); if(ret < 0) return ret; } @@ -195,7 +225,7 @@ static int reset_acquisition_frames(is_nitro_device_handlers* handlers, uint16_t } // Exit if enough frames have passed, or if there currently is some delay. // Exiting early makes it possible to catch up to the DMA, if we're behind. - } while((frame_diff < diff_target) && ((!(last_frame_counter & (FRAME_BUFFER_SIZE - 1))) || ((internalFrameCount & (FRAME_BUFFER_SIZE - 1)) >= (FRAME_BUFFER_SIZE - 2)))); + } while(((frame_diff < diff_target) && (!(last_frame_counter & (FRAME_BUFFER_SIZE - 1)))) || ((internalFrameCount & (FRAME_BUFFER_SIZE - 1)) >= (FRAME_BUFFER_SIZE - (1 + multiplier)))); // Determine how much time a single frame takes. We'll use it for sleeps const auto curr_time = std::chrono::high_resolution_clock::now(); const std::chrono::duration diff = curr_time - clock_last_reset; @@ -254,7 +284,8 @@ void is_nitro_acquisition_emulator_main_loop(CaptureData* capture_data) { float single_frame_time = 0; uint16_t curr_frame_counter = 0; CaptureScreensType curr_capture_type = capture_data->status.capture_type; - int ret = StartAcquisitionEmulator(handlers, last_frame_counter, single_frame_time, curr_capture_type, usb_device_desc); + CaptureSpeedsType curr_capture_speed = capture_data->status.capture_speed; + int ret = StartAcquisitionEmulator(handlers, last_frame_counter, single_frame_time, curr_capture_type, curr_capture_speed, usb_device_desc); if (ret < 0) { capture_error_print(true, capture_data, "Capture Start: Failed"); return; @@ -264,7 +295,7 @@ void is_nitro_acquisition_emulator_main_loop(CaptureData* capture_data) { auto clock_last_reset = std::chrono::high_resolution_clock::now(); while (capture_data->status.connected && capture_data->status.running) { - frame_wait(single_frame_time, clock_last_reset, curr_frame_counter, last_frame_counter); + frame_wait(single_frame_time, clock_last_reset, curr_capture_type, curr_capture_speed, curr_frame_counter, last_frame_counter); if (single_frame_time > 0) { ret = is_nitro_read_frame_and_output(capture_data, inner_curr_in, curr_capture_type, clock_start); @@ -278,7 +309,7 @@ void is_nitro_acquisition_emulator_main_loop(CaptureData* capture_data) { default_sleep(SLEEP_CHECKS_TIME_MS); } capture_data->status.curr_delay = last_frame_counter % FRAME_BUFFER_SIZE; - ret = reset_acquisition_frames(handlers, curr_frame_counter, last_frame_counter, single_frame_time, clock_last_reset, curr_capture_type, capture_data->status.capture_type, 1, usb_device_desc); + ret = reset_acquisition_frames(handlers, curr_frame_counter, last_frame_counter, single_frame_time, clock_last_reset, curr_capture_type, capture_data->status.capture_type, curr_capture_speed, capture_data->status.capture_speed, usb_device_desc); if (ret < 0) { capture_error_print(true, capture_data, "Disconnected: Frame counter reset error"); break; diff --git a/source/Menus/ISNitroMenu.cpp b/source/Menus/ISNitroMenu.cpp index f87705b..49afa40 100644 --- a/source/Menus/ISNitroMenu.cpp +++ b/source/Menus/ISNitroMenu.cpp @@ -28,6 +28,12 @@ static const ISNitroMenuOptionInfo is_nitro_type_option = { .is_inc = true, .dec_str = "<", .inc_str = ">", .inc_out_action = ISN_MENU_TYPE_INC, .out_action = ISN_MENU_TYPE_DEC}; +static const ISNitroMenuOptionInfo is_nitro_speed_option = { +.base_name = "Speed", .false_name = "", .is_selectable = true, +.is_capture_valid = true, .is_emulator_valid = true, +.is_inc = true, .dec_str = "<", .inc_str = ">", .inc_out_action = ISN_MENU_SPEED_INC, +.out_action = ISN_MENU_SPEED_DEC}; + static const ISNitroMenuOptionInfo is_nitro_reset_option = { .base_name = "Reset Hardware", .false_name = "", .is_selectable = true, .is_capture_valid = true, .is_emulator_valid = false, @@ -37,6 +43,7 @@ static const ISNitroMenuOptionInfo is_nitro_reset_option = { static const ISNitroMenuOptionInfo* pollable_options[] = { &is_nitro_delay_option, &is_nitro_type_option, +&is_nitro_speed_option, &is_nitro_reset_option, }; @@ -132,6 +139,19 @@ static std::string get_capture_type_name(CaptureScreensType capture_type) { } } +static std::string get_capture_speed_name(CaptureSpeedsType capture_speed) { + switch(capture_speed) { + case CAPTURE_SPEEDS_HALF: + return "0.5x"; + case CAPTURE_SPEEDS_THIRD: + return "0.33x"; + case CAPTURE_SPEEDS_QUARTER: + return "0.25x"; + default: + return "1.0x"; + } +} + void ISNitroMenu::prepare(float menu_scaling_factor, int view_size_x, int view_size_y, CaptureStatus* capture_status) { int num_pages = this->get_num_pages(); if(this->future_data.page >= num_pages) @@ -150,6 +170,9 @@ void ISNitroMenu::prepare(float menu_scaling_factor, int view_size_x, int view_s case ISN_MENU_TYPE_DEC: this->labels[index]->setText(this->setTextOptionString(real_index, get_capture_type_name(capture_status->capture_type))); break; + case ISN_MENU_SPEED_DEC: + this->labels[index]->setText(this->setTextOptionString(real_index, get_capture_speed_name(capture_status->capture_speed))); + break; default: break; } diff --git a/source/WindowScreen_Menu.cpp b/source/WindowScreen_Menu.cpp index 1fe2d29..4b0f20b 100755 --- a/source/WindowScreen_Menu.cpp +++ b/source/WindowScreen_Menu.cpp @@ -185,6 +185,15 @@ void WindowScreen::is_nitro_capture_type_change(bool positive) { this->capture_status->capture_type = static_cast(new_value % CAPTURE_SCREENS_ENUM_END); } +void WindowScreen::is_nitro_capture_speed_change(bool positive) { + int new_value = (int)(this->capture_status->capture_speed); + if(positive) + new_value += 1; + else + new_value += CAPTURE_SPEEDS_ENUM_END - 1; + this->capture_status->capture_speed = static_cast(new_value % CAPTURE_SPEEDS_ENUM_END); +} + void WindowScreen::is_nitro_capture_reset_hw() { this->capture_status->reset_hardware = true; } @@ -1819,6 +1828,12 @@ void WindowScreen::poll(bool do_everything) { case ISN_MENU_TYPE_INC: this->is_nitro_capture_type_change(true); break; + case ISN_MENU_SPEED_DEC: + this->is_nitro_capture_speed_change(false); + break; + case ISN_MENU_SPEED_INC: + this->is_nitro_capture_speed_change(true); + break; case ISN_MENU_RESET: this->is_nitro_capture_reset_hw(); break; diff --git a/source/cc3dsfs.cpp b/source/cc3dsfs.cpp index 5539410..3c8fd97 100755 --- a/source/cc3dsfs.cpp +++ b/source/cc3dsfs.cpp @@ -112,6 +112,11 @@ static bool load(const std::string path, const std::string name, ScreenInfo &top continue; } + if(key == "is_speed_capture") { + capture_status->capture_speed = static_cast(std::stoi(value) % CaptureSpeedsType::CAPTURE_SPEEDS_ENUM_END); + continue; + } + if(audio_data->load_audio_data(key, value)) continue; } @@ -130,6 +135,7 @@ static bool load(const std::string path, const std::string name, ScreenInfo &top static void defaults_reload(FrontendData *frontend_data, AudioData* audio_data, ExtraButtonShortcuts* extra_button_shortcuts, CaptureStatus* capture_status) { capture_status->enabled_3d = false; capture_status->capture_type = CAPTURE_SCREENS_BOTH; + capture_status->capture_speed = CAPTURE_SPEEDS_FULL; reset_screen_info(frontend_data->top_screen->m_info); reset_screen_info(frontend_data->bot_screen->m_info); reset_screen_info(frontend_data->joint_screen->m_info); @@ -188,6 +194,7 @@ static bool save(const std::string path, const std::string name, const std::stri file << "extra_button_enter_short=" << extra_button_shortcuts->enter_shortcut->cmd << std::endl; file << "extra_button_page_up_short=" << extra_button_shortcuts->page_up_shortcut->cmd << std::endl; file << "is_screen_capture_type=" << capture_status->capture_type << std::endl; + file << "is_speed_capture=" << capture_status->capture_speed << std::endl; file << audio_data->save_audio_data(); file.close(); @@ -294,6 +301,23 @@ static void check_close_application(WindowScreen *screen, CaptureData* capture_d } } +static float get_time_multiplier(CaptureData* capture_data, bool should_ignore_data_rate) { + if(should_ignore_data_rate) + return 1.0; + if(capture_data->status.device.cc_type != CAPTURE_CONN_IS_NITRO) + return 1.0; + switch(capture_data->status.capture_speed) { + case CAPTURE_SPEEDS_HALF: + return 2.0; + case CAPTURE_SPEEDS_THIRD: + return 3.0; + case CAPTURE_SPEEDS_QUARTER: + return 4.0; + default: + return 1.0; + } +} + static int mainVideoOutputCall(AudioData* audio_data, CaptureData* capture_data, bool mono_app) { VideoOutputData *out_buf; double last_frame_time = 0.0; @@ -340,6 +364,7 @@ static int mainVideoOutputCall(AudioData* audio_data, CaptureData* capture_data, capture_data->status.connected = connect(true, capture_data, &frontend_data); bool last_connected = capture_data->status.connected; SuccessConnectionOutTextGenerator(out_text_data, capture_data); + int no_data_consecutive = 0; while(capture_data->status.running) { @@ -357,28 +382,36 @@ static int mainVideoOutputCall(AudioData* audio_data, CaptureData* capture_data, if(!last_connected) update_connected_specific_settings(&frontend_data, capture_data->status.device); last_connected = true; + if(no_data_consecutive > 60) + no_data_consecutive = 60; + capture_data->status.video_wait.update_time_multiplier(get_time_multiplier(capture_data, no_data_consecutive > 3)); bool timed_out = !capture_data->status.video_wait.timed_lock(); curr_out = (capture_data->status.curr_in - 1 + NUM_CONCURRENT_DATA_BUFFERS) % NUM_CONCURRENT_DATA_BUFFERS; - if((!capture_data->status.cooldown_curr_in) && (curr_out != prev_out)) { + if(curr_out != prev_out) { last_frame_time = capture_data->time_in_buf[curr_out]; if(capture_data->read[curr_out] >= get_video_in_size(capture_data)) { - bool conversion_success = convertVideoToOutput(curr_out, out_buf, capture_data); - if(!conversion_success) - UpdateOutText(out_text_data, "", "Video conversion failed...", TEXT_KIND_NORMAL); + if(capture_data->status.cooldown_curr_in) + blank_out = true; + else { + bool conversion_success = convertVideoToOutput(curr_out, out_buf, capture_data); + if(!conversion_success) + UpdateOutText(out_text_data, "", "Video conversion failed...", TEXT_KIND_NORMAL); + } num_allowed_blanks = MAX_ALLOWED_BLANKS; + no_data_consecutive = 0; } else { - if(num_allowed_blanks > 0) { + if(num_allowed_blanks > 0) num_allowed_blanks--; - } - else { - memset(out_buf, 0, sizeof(VideoOutputData)); - } + else + blank_out = true; } } - else if((capture_data->status.cooldown_curr_in) || timed_out) + else if(timed_out) { blank_out = true; + no_data_consecutive++; + } prev_out = curr_out; } else { diff --git a/source/utils.cpp b/source/utils.cpp index 776cc57..15bf3f9 100755 --- a/source/utils.cpp +++ b/source/utils.cpp @@ -212,7 +212,13 @@ std::string load_layout_name(int index, bool &success) { ConsumerMutex::ConsumerMutex() { count = 0; } - + +void ConsumerMutex::update_time_multiplier(float time_multiplier) { + if(time_multiplier <= 0) + return; + this->time_multiplier = time_multiplier; +} + void ConsumerMutex::lock() { access_mutex.lock(); bool success = false; @@ -229,6 +235,7 @@ void ConsumerMutex::lock() { } bool ConsumerMutex::timed_lock() { + std::chrono::durationmax_timed_wait = std::chrono::duration(1.0 / ((base_time_fps) * (1.0 / this->time_multiplier))); access_mutex.lock(); bool success = false; while(!success) {