diff --git a/.github/workflows/cd_main.yml b/.github/workflows/cd_main.yml index bd26ccf..84aefa5 100644 --- a/.github/workflows/cd_main.yml +++ b/.github/workflows/cd_main.yml @@ -16,20 +16,20 @@ jobs: fail-fast: false matrix: platform: - - { name: Windows VS2022 x64, os: windows-2022, flags: -A x64} - - { name: Windows VS2022 ARM, os: windows-2022, flags: -A ARM64} - - { name: Windows VS2022 Win32, os: windows-2022, flags: -A Win32} - - { name: Linux GCC x64, os: ubuntu-latest, flags: 64} - - { name: Linux GCC 32, os: ubuntu-latest, flags: 32} - - { name: Linux GCC ARM 64, os: ubuntu-latest, flags: arm64} - - { name: Linux GCC ARM 32, os: ubuntu-latest, flags: arm32} - - { name: macOS Apple Silicon, os: macos-14 } + - { name: Windows VS2022 x64, os: windows-2022, flags: -A x64, artifact_name: win64} + - { name: Windows VS2022 ARM, os: windows-2022, flags: -A ARM64, artifact_name: winarm64} + - { name: Windows VS2022 Win32, os: windows-2022, flags: -A Win32, artifact_name: win32} + - { name: Linux GCC x64, os: ubuntu-latest, flags: 64, artifact_name: linux64} + - { name: Linux GCC 32, os: ubuntu-latest, flags: 32, artifact_name: linux32} + - { name: Linux GCC ARM 64, os: ubuntu-latest, flags: arm64, artifact_name: linuxarm64} + - { name: Linux GCC ARM 32, os: ubuntu-latest, flags: arm32, artifact_name: linuxarm32} + - { name: macOS Apple Silicon, os: macos-14, artifact_name: macos } config: - { name: Static, flags: -DBUILD_SHARED_LIBS=FALSE } steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Configure if: runner.os != 'Linux' @@ -61,9 +61,9 @@ jobs: run: setup_scripts/macos_bundle_signature.sh cc3dsfs_macos - name: Archive resulting artifact - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: - name: cc3dsfs_zip + name: cc3dsfs-single-${{matrix.platform.artifact_name}} path: build/cc3dsfs*.zip create_pi_setup: @@ -72,21 +72,22 @@ jobs: needs: build steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 - - name: Download a single artifact - uses: actions/download-artifact@v3 + - name: Download all Linux ARM artifacts + uses: actions/download-artifact@v4 with: - name: cc3dsfs_zip + pattern: cc3dsfs-single-linuxarm* + merge-multiple: true - name: Execute pi_setup shell: bash run: setup_scripts/rpi_mono_setup_setup.sh - name: Archive resulting artifact - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: - name: cc3dsfs_zip + name: cc3dsfs-single-rpi-mono path: cc3dsfs_rpi_mono_setup.zip publish: @@ -96,13 +97,14 @@ jobs: permissions: contents: write steps: - - name: Download a single artifact - uses: actions/download-artifact@v3 + - name: Download all artifacts + uses: actions/download-artifact@v4 with: - name: cc3dsfs_zip + pattern: cc3dsfs-single-* + merge-multiple: true - name: Auto-Release the zips - uses: IsaacShelton/update-existing-release@v1.3.2 + uses: IsaacShelton/update-existing-release@v1.3.4 with: token: ${{ secrets.GITHUB_TOKEN }} release: "Automatic Build" diff --git a/.github/workflows/cd_pr.yml b/.github/workflows/cd_pr.yml index b51e082..473f8ac 100644 --- a/.github/workflows/cd_pr.yml +++ b/.github/workflows/cd_pr.yml @@ -11,20 +11,20 @@ jobs: fail-fast: false matrix: platform: - - { name: Windows VS2022 x64, os: windows-2022, flags: -A x64} - - { name: Windows VS2022 ARM, os: windows-2022, flags: -A ARM64} - - { name: Windows VS2022 Win32, os: windows-2022, flags: -A Win32} - - { name: Linux GCC x64, os: ubuntu-latest, flags: 64} - - { name: Linux GCC 32, os: ubuntu-latest, flags: 32} - - { name: Linux GCC ARM 64, os: ubuntu-latest, flags: arm64} - - { name: Linux GCC ARM 32, os: ubuntu-latest, flags: arm32} - - { name: macOS Apple Silicon, os: macos-14 } + - { name: Windows VS2022 x64, os: windows-2022, flags: -A x64, artifact_name: win64} + - { name: Windows VS2022 ARM, os: windows-2022, flags: -A ARM64, artifact_name: winarm64} + - { name: Windows VS2022 Win32, os: windows-2022, flags: -A Win32, artifact_name: win32} + - { name: Linux GCC x64, os: ubuntu-latest, flags: 64, artifact_name: linux64} + - { name: Linux GCC 32, os: ubuntu-latest, flags: 32, artifact_name: linux32} + - { name: Linux GCC ARM 64, os: ubuntu-latest, flags: arm64, artifact_name: linuxarm64} + - { name: Linux GCC ARM 32, os: ubuntu-latest, flags: arm32, artifact_name: linuxarm32} + - { name: macOS Apple Silicon, os: macos-14, artifact_name: macos } config: - { name: Static, flags: -DBUILD_SHARED_LIBS=FALSE } steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Configure if: runner.os != 'Linux' @@ -56,9 +56,9 @@ jobs: run: setup_scripts/macos_bundle_signature.sh cc3dsfs_macos - name: Archive resulting artifact - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: - name: cc3dsfs_zip + name: cc3dsfs-single-${{matrix.platform.artifact_name}} path: build/cc3dsfs*.zip create_pi_setup: @@ -67,19 +67,20 @@ jobs: needs: build steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 - - name: Download a single artifact - uses: actions/download-artifact@v3 + - name: Download all Linux ARM artifacts + uses: actions/download-artifact@v4 with: - name: cc3dsfs_zip + pattern: cc3dsfs-single-linuxarm* + merge-multiple: true - name: Execute pi_setup shell: bash run: setup_scripts/rpi_mono_setup_setup.sh - name: Archive resulting artifact - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: - name: cc3dsfs_zip + name: cc3dsfs-single-rpi-mono path: cc3dsfs_rpi_mono_setup.zip diff --git a/include/CaptureDeviceSpecific/ISNitro/usb_is_nitro_communications.hpp b/include/CaptureDeviceSpecific/ISNitro/usb_is_nitro_communications.hpp index f960e4c..747bb06 100644 --- a/include/CaptureDeviceSpecific/ISNitro/usb_is_nitro_communications.hpp +++ b/include/CaptureDeviceSpecific/ISNitro/usb_is_nitro_communications.hpp @@ -4,6 +4,7 @@ #include #include #include +#include #include "utils.hpp" #define IS_NITRO_REAL_SERIAL_NUMBER_SIZE 10 diff --git a/include/audio.hpp b/include/audio.hpp index d9cdd53..62a5084 100755 --- a/include/audio.hpp +++ b/include/audio.hpp @@ -7,11 +7,11 @@ #include "utils.hpp" struct Sample { - Sample(std::int16_t *bytes, std::size_t size, float time); + Sample(std::int16_t *bytes, std::size_t size, double time) : bytes(bytes), size(size), time(time) {} std::int16_t *bytes; std::size_t size; - float time; + double time; }; class Audio : public sf::SoundStream { diff --git a/include/capture_structs.hpp b/include/capture_structs.hpp index f1747c7..eb464f3 100755 --- a/include/capture_structs.hpp +++ b/include/capture_structs.hpp @@ -17,7 +17,7 @@ #define EXTRA_DATA_BUFFER_FTD3XX_SIZE (1 << 10) enum CaptureConnectionType { CAPTURE_CONN_FTD3, CAPTURE_CONN_USB, CAPTURE_CONN_FTD2, CAPTURE_CONN_IS_NITRO }; -enum InputVideoDataType { VIDEO_DATA_RGB, VIDEO_DATA_BGR, VIDEO_DATA_RGB16 }; +enum InputVideoDataType { VIDEO_DATA_RGB, VIDEO_DATA_BGR, VIDEO_DATA_RGB16, VIDEO_DATA_BGR16 }; 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 }; @@ -57,25 +57,25 @@ struct PACKED ISNitroEmulatorVideoInputData { uint8_t screen_data[IN_VIDEO_SIZE_DS][3]; }; -struct ALIGNED(4) PACKED FTD3_3DSCaptureReceived { +struct ALIGNED(16) PACKED FTD3_3DSCaptureReceived { RGB83DSVideoInputData video_in; uint16_t audio_data[N3DSXL_SAMPLES_IN]; uint8_t unused_buffer[EXTRA_DATA_BUFFER_FTD3XX_SIZE]; }; -struct ALIGNED(4) PACKED FTD3_3DSCaptureReceived_3D { +struct ALIGNED(16) PACKED FTD3_3DSCaptureReceived_3D { RGB83DSVideoInputData_3D video_in; uint16_t audio_data[N3DSXL_SAMPLES_IN]; uint8_t unused_buffer[EXTRA_DATA_BUFFER_FTD3XX_SIZE]; }; -struct ALIGNED(4) PACKED USB3DSCaptureReceived { +struct ALIGNED(16) PACKED USB3DSCaptureReceived { RGB83DSVideoInputData video_in; uint16_t audio_data[O3DS_SAMPLES_IN]; uint8_t unused_buffer[EXTRA_DATA_BUFFER_USB_SIZE]; }; -struct ALIGNED(4) PACKED USB3DSCaptureReceived_3D { +struct ALIGNED(16) PACKED USB3DSCaptureReceived_3D { RGB83DSVideoInputData_3D video_in; uint16_t audio_data[O3DS_SAMPLES_IN]; uint8_t unused_buffer[EXTRA_DATA_BUFFER_USB_SIZE]; @@ -88,13 +88,13 @@ struct PACKED USBOldDSFrameInfo { uint8_t unused[11]; }; -struct ALIGNED(4) PACKED USBOldDSCaptureReceived { +struct ALIGNED(16) PACKED USBOldDSCaptureReceived { USBOldDSVideoInputData video_in; uint8_t unused_buffer[EXTRA_DATA_BUFFER_USB_SIZE]; USBOldDSFrameInfo frameinfo; }; -struct ALIGNED(4) PACKED ISNitroCaptureReceived { +struct ALIGNED(16) PACKED ISNitroCaptureReceived { ISNitroEmulatorVideoInputData video_in; }; diff --git a/include/display_structs.hpp b/include/display_structs.hpp index d696fcc..bc828ca 100755 --- a/include/display_structs.hpp +++ b/include/display_structs.hpp @@ -48,7 +48,7 @@ struct DisplayData { bool last_connected_ds; }; -struct VideoOutputData { +struct ALIGNED(8) VideoOutputData { uint8_t screen_data[MAX_IN_VIDEO_SIZE][3]; }; diff --git a/include/utils.hpp b/include/utils.hpp index eaada50..2ea556b 100755 --- a/include/utils.hpp +++ b/include/utils.hpp @@ -8,9 +8,11 @@ #if _MSC_VER && !__INTEL_COMPILER #define PACKED #define ALIGNED(x) alignas(x) +#define STDCALL __stdcall #else #define PACKED __attribute__((packed)) #define ALIGNED(x) __attribute__((aligned(x))) +#define STDCALL #endif #define NAME "cc3dsfs" diff --git a/source/CaptureDeviceSpecific/ISNitro/usb_is_nitro_acquisition_emulator.cpp b/source/CaptureDeviceSpecific/ISNitro/usb_is_nitro_acquisition_emulator.cpp index 3bf4bd6..7206be7 100644 --- a/source/CaptureDeviceSpecific/ISNitro/usb_is_nitro_acquisition_emulator.cpp +++ b/source/CaptureDeviceSpecific/ISNitro/usb_is_nitro_acquisition_emulator.cpp @@ -143,6 +143,8 @@ static bool do_sleep(float single_frame_time, std::chrono::time_pointtransfer_data_access.lock(); if ((error == LIBUSB_SUCCESS) && (num_bytes != cb_data->requested_length)) error = LIBUSB_ERROR_INTERRUPTED; @@ -159,7 +159,7 @@ static void BasicCompletionOutputRoutine(isn_async_callback_data* cb_data, int n cb_data->transfer_data_access.unlock(); } -static void OverlappedCompletionNothingRoutine(DWORD dwErrorCode, DWORD dwNumberOfBytesTransfered, OVERLAPPED* overlapped_var) { +static void STDCALL OverlappedCompletionNothingRoutine(DWORD dwErrorCode, DWORD dwNumberOfBytesTransfered, OVERLAPPED* overlapped_var) { return; } @@ -309,4 +309,4 @@ void is_nitro_is_driver_cancel_callback(isn_async_callback_data* cb_data) { #ifdef _WIN32 // Nothing #endif -} \ No newline at end of file +} diff --git a/source/CaptureDeviceSpecific/ISNitro/usb_is_nitro_libusb.cpp b/source/CaptureDeviceSpecific/ISNitro/usb_is_nitro_libusb.cpp index 1748d01..7cf87ee 100644 --- a/source/CaptureDeviceSpecific/ISNitro/usb_is_nitro_libusb.cpp +++ b/source/CaptureDeviceSpecific/ISNitro/usb_is_nitro_libusb.cpp @@ -169,7 +169,7 @@ void is_nitro_libusb_cancell_callback(isn_async_callback_data* cb_data) { cb_data->transfer_data_access.unlock(); } -void is_nitro_libusb_async_callback(libusb_transfer* transfer) { +void STDCALL is_nitro_libusb_async_callback(libusb_transfer* transfer) { isn_async_callback_data* cb_data = (isn_async_callback_data*)transfer->user_data; cb_data->transfer_data_access.lock(); cb_data->transfer_data = NULL; @@ -190,4 +190,4 @@ void is_nitro_libusb_async_in_start(is_nitro_device_handlers* handlers, const is transfer_in->flags |= LIBUSB_TRANSFER_FREE_TRANSFER; libusb_submit_transfer(transfer_in); cb_data->transfer_data_access.unlock(); -} \ No newline at end of file +} diff --git a/source/CaptureDeviceSpecific/usb_ds_3ds_capture.cpp b/source/CaptureDeviceSpecific/usb_ds_3ds_capture.cpp index 061310e..2b18827 100644 --- a/source/CaptureDeviceSpecific/usb_ds_3ds_capture.cpp +++ b/source/CaptureDeviceSpecific/usb_ds_3ds_capture.cpp @@ -18,6 +18,9 @@ #define SIMPLE_DS_FRAME_SKIP +#define NUM_DS_3DS_CONCURRENT_DATA_BUFFERS 4 +#define NUM_DS_3DS_CONCURRENT_REQUESTS 4 + enum usb_capture_status { USB_CAPTURE_SUCCESS = 0, USB_CAPTURE_SKIP, @@ -51,6 +54,19 @@ struct usb_ds_status { uint8_t capture_in_progress; }; +struct usb_ds_3ds_general_data { + SharedConsumerMutex* is_there_ready_buffer; + SharedConsumerMutex* is_buffer_usage_done; + uint32_t index; + uint32_t* last_index; + int real_index; + bool in_use; + CaptureData* capture_data; + CaptureReceived capture_buf; + size_t read_amount; + double time_in; +}; + static const usb_device usb_3ds_desc = { .is_3ds = true, .vid = 0x16D0, .pid = 0x06A3, @@ -80,6 +96,51 @@ static const usb_device* usb_devices_desc_list[] = { &usb_old_ds_desc, }; +static void unlock_buffer(usb_ds_3ds_general_data* buffer_data) { + buffer_data->in_use = false; + buffer_data->is_buffer_usage_done->specific_unlock(buffer_data->real_index); +} + +static void ds_3ds_usb_thread_function(bool* usb_thread_run, usb_ds_3ds_general_data* buffer_data) { + if(!usb_is_initialized()) + return; + while(*usb_thread_run) { + int processing_index = *buffer_data[0].last_index; + int dummy = 0; + for(int i = 0; i < NUM_DS_3DS_CONCURRENT_DATA_BUFFERS; i++) { + if(!buffer_data[i].in_use) + continue; + int diff = (int)(processing_index - buffer_data[i].index); + if(diff < 0) + unlock_buffer(&buffer_data[i]); + else if(diff == 0) { + buffer_data[i].capture_data->data_buffers.WriteToBuffer(&buffer_data[i].capture_buf, buffer_data[i].read_amount, buffer_data[i].time_in, &buffer_data[i].capture_data->status.device); + if(buffer_data[i].capture_data->status.cooldown_curr_in) + buffer_data[i].capture_data->status.cooldown_curr_in = buffer_data[i].capture_data->status.cooldown_curr_in - 1; + buffer_data[i].capture_data->status.video_wait.unlock(); + buffer_data[i].capture_data->status.audio_wait.unlock(); + unlock_buffer(&buffer_data[i]); + } + } + buffer_data[0].is_there_ready_buffer->general_timed_lock(&dummy); + } +} + +static void ds_3ds_start_thread(std::thread* thread_ptr, bool* usb_thread_run, usb_ds_3ds_general_data* buffer_data) { + if(!usb_is_initialized()) + return; + *usb_thread_run = true; + *thread_ptr = std::thread(ds_3ds_usb_thread_function, usb_thread_run, buffer_data); +} + +static void ds_3ds_close_thread(std::thread* thread_ptr, bool* usb_thread_run, usb_ds_3ds_general_data* buffer_data) { + if(!usb_is_initialized()) + return; + *usb_thread_run = false; + buffer_data[0].is_there_ready_buffer->specific_unlock(0); + thread_ptr->join(); +} + // Read vendor request from control endpoint. Returns bytes transferred (<0 = libusb error) static int vend_in(libusb_device_handle *handle, const usb_device* usb_device_desc, uint8_t bRequest, uint16_t wValue, uint16_t wIndex, uint16_t wLength, uint8_t *buf) { return libusb_control_transfer(handle, ((uint8_t)LIBUSB_REQUEST_TYPE_VENDOR | (uint8_t)LIBUSB_ENDPOINT_IN), bRequest, wValue, wIndex, buf, wLength, usb_device_desc->control_timeout); @@ -212,11 +273,13 @@ static uint64_t get_capture_size(const usb_device* usb_device_desc, bool enabled return sizeof(USB3DSCaptureReceived) - EXTRA_DATA_BUFFER_USB_SIZE; } -static usb_capture_status capture_read_oldds_3ds(CaptureData* capture_data, CaptureReceived* data_buffer, int &bytesIn) { +static usb_capture_status capture_read_oldds_3ds(usb_ds_3ds_general_data* chosen_buffer) { + CaptureData* capture_data = chosen_buffer->capture_data; + CaptureReceived* data_buffer = &chosen_buffer->capture_buf; libusb_device_handle* handle = (libusb_device_handle*)capture_data->handle; const usb_device* usb_device_desc = get_usb_device_desc(capture_data); const bool enabled_3d = capture_data->status.enabled_3d; - bytesIn = 0; + int bytesIn = 0; int transferred = 0; int result = 0; uint64_t video_size = _usb_get_video_in_size(usb_device_desc, enabled_3d); @@ -264,6 +327,7 @@ static usb_capture_status capture_read_oldds_3ds(CaptureData* capture_data, Capt #endif } + chosen_buffer->read_amount = bytesIn; return USB_CAPTURE_SUCCESS; } @@ -316,6 +380,70 @@ static void usb_3DSconvertVideoToOutput(USB3DSCaptureReceived *p_in, VideoOutput memcpy(p_out->screen_data, p_in->video_in.screen_data, IN_VIDEO_HEIGHT_3DS * IN_VIDEO_WIDTH_3DS * 3); } +static void process_usb_capture_result(usb_capture_status result, std::chrono::time_point* clock_start, bool* done, usb_ds_3ds_general_data* chosen_buffer) { + const auto curr_time = std::chrono::high_resolution_clock::now(); + const std::chrono::duration diff = curr_time - (*clock_start); + + switch(result) { + case USB_CAPTURE_SKIP: + /* + if(diff.count() >= CAPTURE_SKIP_TIMEOUT_SECONDS) { + capture_error_print(true, chosen_buffer->capture_data, "Disconnected: Too long since last read"); + done = true; + } + */ + break; + case USB_CAPTURE_PIPE_ERROR: + capture_error_print(true, chosen_buffer->capture_data, "Disconnected: Pipe error"); + *done = true; + break; + case USB_CAPTURE_FRAMEINFO_ERROR: + capture_error_print(true, chosen_buffer->capture_data, "Disconnected: Frameinfo error"); + *done = true; + break; + case USB_CAPTURE_SUCCESS: + *clock_start = curr_time; + chosen_buffer->time_in = diff.count(); + chosen_buffer->index = (*chosen_buffer->last_index) + 1; + chosen_buffer->in_use = true; + (*chosen_buffer->last_index) += 1; + chosen_buffer->is_there_ready_buffer->specific_unlock(chosen_buffer->real_index); + break; + default: + capture_error_print(true, chosen_buffer->capture_data, "Disconnected: Error"); + *done = true; + break; + } +} + +static usb_ds_3ds_general_data* get_free_buffer(usb_ds_3ds_general_data* buffer_data) { + usb_ds_3ds_general_data* out_data = NULL; + int dummy = 0; + while(!out_data) { + for(int i = 0; i < NUM_DS_3DS_CONCURRENT_DATA_BUFFERS; i++) { + if(!buffer_data[i].in_use) { + buffer_data[i].is_there_ready_buffer->specific_try_lock(i); + out_data = &buffer_data[i]; + break; + } + } + if(!out_data) + buffer_data[0].is_there_ready_buffer->general_timed_lock(&dummy); + } + return out_data; +} + +static void wait_all_free_buffer(usb_ds_3ds_general_data* buffer_data) { + for(int i = 0; i < NUM_DS_3DS_CONCURRENT_DATA_BUFFERS; i++) + buffer_data[i].index = (*buffer_data[i].last_index) - 1; + for(int i = 0; i < NUM_DS_3DS_CONCURRENT_DATA_BUFFERS; i++) { + while(buffer_data[i].in_use) { + buffer_data[i].is_there_ready_buffer->specific_unlock(i); + buffer_data[i].is_there_ready_buffer->specific_lock(i); + } + } +} + void list_devices_usb_ds_3ds(std::vector &devices_list, std::vector &no_access_list) { if(!usb_is_initialized()) return; @@ -389,48 +517,36 @@ uint64_t usb_get_video_in_size(CaptureData* capture_data) { void usb_capture_main_loop(CaptureData* capture_data) { if(!usb_is_initialized()) return; - CaptureReceived* capture_buf = new CaptureReceived; - auto clock_start = std::chrono::high_resolution_clock::now(); + + std::chrono::time_point clock_start = std::chrono::high_resolution_clock::now(); bool done = false; - - while((!done) && capture_data->status.connected && capture_data->status.running) { - - int read_amount = 0; - usb_capture_status result = capture_read_oldds_3ds(capture_data, capture_buf, read_amount); - - const auto curr_time = std::chrono::high_resolution_clock::now(); - const std::chrono::duration diff = curr_time - clock_start; - - switch(result) { - case USB_CAPTURE_SKIP: - if(diff.count() >= CAPTURE_SKIP_TIMEOUT_SECONDS) { - capture_error_print(true, capture_data, "Disconnected: Too long since last read"); - done = true; - } - break; - case USB_CAPTURE_PIPE_ERROR: - capture_error_print(true, capture_data, "Disconnected: Pipe error"); - done = true; - break; - case USB_CAPTURE_FRAMEINFO_ERROR: - capture_error_print(true, capture_data, "Disconnected: Frameinfo error"); - done = true; - break; - case USB_CAPTURE_SUCCESS: - clock_start = curr_time; - capture_data->data_buffers.WriteToBuffer(capture_buf, read_amount, diff.count(), &capture_data->status.device); - if(capture_data->status.cooldown_curr_in) - capture_data->status.cooldown_curr_in = capture_data->status.cooldown_curr_in - 1; - capture_data->status.video_wait.unlock(); - capture_data->status.audio_wait.unlock(); - break; - default: - capture_error_print(true, capture_data, "Disconnected: Error"); - done = true; - break; - } + usb_ds_3ds_general_data* general_data = new usb_ds_3ds_general_data[NUM_DS_3DS_CONCURRENT_DATA_BUFFERS]; + SharedConsumerMutex is_there_ready_buffer(NUM_DS_3DS_CONCURRENT_DATA_BUFFERS); + SharedConsumerMutex is_buffer_usage_done(NUM_DS_3DS_CONCURRENT_DATA_BUFFERS); + uint32_t last_index = -1; + for(int i = 0; i < NUM_DS_3DS_CONCURRENT_DATA_BUFFERS; i++) { + general_data[i].is_there_ready_buffer = &is_there_ready_buffer; + general_data[i].is_buffer_usage_done = &is_buffer_usage_done; + general_data[i].last_index = &last_index; + general_data[i].capture_data = capture_data; + general_data[i].in_use = false; + general_data[i].real_index = i; } - delete capture_buf; + bool usb_thread_run; + std::thread processing_thread; + ds_3ds_start_thread(&processing_thread, &usb_thread_run, general_data); + + usb_ds_3ds_general_data* chosen_buffer = get_free_buffer(general_data); + while((!done) && capture_data->status.connected && capture_data->status.running) { + usb_capture_status result = capture_read_oldds_3ds(chosen_buffer); + + process_usb_capture_result(result, &clock_start, &done, chosen_buffer); + if(!done) + chosen_buffer = get_free_buffer(general_data); + } + wait_all_free_buffer(general_data); + ds_3ds_close_thread(&processing_thread, &usb_thread_run, general_data); + delete []general_data; } void usb_capture_cleanup(CaptureData* capture_data) { diff --git a/source/WindowScreen.cpp b/source/WindowScreen.cpp index bc3dc17..74b061b 100755 --- a/source/WindowScreen.cpp +++ b/source/WindowScreen.cpp @@ -441,6 +441,10 @@ std::string WindowScreen::title_factory() { #define GL_UNSIGNED_SHORT_5_6_5 0x8363 #endif +#ifndef GL_UNSIGNED_SHORT_5_6_5_REV +#define GL_UNSIGNED_SHORT_5_6_5_REV 0x8364 +#endif + void WindowScreen::update_texture() { unsigned int m_texture = this->in_tex.getNativeHandle(); if (this->saved_buf && m_texture) @@ -453,6 +457,8 @@ void WindowScreen::update_texture() { format = GL_BGR; if(this->capture_status->device.video_data_type == VIDEO_DATA_RGB16) type = GL_UNSIGNED_SHORT_5_6_5; + if(this->capture_status->device.video_data_type == VIDEO_DATA_BGR16) + type = GL_UNSIGNED_SHORT_5_6_5_REV; glTexSubImage2D(GL_TEXTURE_2D, 0, static_cast(0), static_cast(0), static_cast(this->capture_status->device.width), static_cast(this->capture_status->device.height), format, type, this->saved_buf); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); diff --git a/source/audio.cpp b/source/audio.cpp index bb61f78..6bdefa5 100755 --- a/source/audio.cpp +++ b/source/audio.cpp @@ -18,8 +18,6 @@ static int greatest_diff = 0; #endif -Sample::Sample(std::int16_t *bytes, std::size_t size, float time) : bytes(bytes), size(size), time(time) {} - //============================================================================ Audio::Audio(AudioData *audio_data) { diff --git a/source/cc3dsfs.cpp b/source/cc3dsfs.cpp index 03bf0e6..c4da17d 100755 --- a/source/cc3dsfs.cpp +++ b/source/cc3dsfs.cpp @@ -381,14 +381,19 @@ static int mainVideoOutputCall(AudioData* audio_data, CaptureData* capture_data, poll_timeout = LOW_POLL_DIVISOR; VideoOutputData *chosen_buf = out_buf; bool blank_out = false; - if(capture_data->status.connected) { - if(!last_connected) - update_connected_specific_settings(&frontend_data, capture_data->status.device); - last_connected = true; + bool is_connected = capture_data->status.connected; + if(is_connected != last_connected) { + update_connected_specific_settings(&frontend_data, capture_data->status.device); + no_data_consecutive = 0; + num_allowed_blanks = MAX_ALLOWED_BLANKS; + } + last_connected = is_connected; + if(is_connected) { if(no_data_consecutive > NO_DATA_CONSECUTIVE_THRESHOLD) no_data_consecutive = NO_DATA_CONSECUTIVE_THRESHOLD; capture_data->status.video_wait.update_time_multiplier(get_time_multiplier(capture_data, no_data_consecutive >= NO_DATA_CONSECUTIVE_THRESHOLD)); bool timed_out = !capture_data->status.video_wait.timed_lock(); + bool data_processed = false; CaptureDataSingleBuffer* data_buffer = capture_data->data_buffers.GetReaderBuffer(CAPTURE_READER_VIDEO); if(data_buffer != NULL) { @@ -403,24 +408,19 @@ static int mainVideoOutputCall(AudioData* audio_data, CaptureData* capture_data, } num_allowed_blanks = MAX_ALLOWED_BLANKS; no_data_consecutive = 0; - } - else { - if(num_allowed_blanks > 0) - num_allowed_blanks--; - else - blank_out = true; + data_processed = true; } capture_data->data_buffers.ReleaseReaderBuffer(CAPTURE_READER_VIDEO); } - else if(timed_out) { - blank_out = true; + if(!data_processed) { + if(num_allowed_blanks > 0) + num_allowed_blanks--; + else + blank_out = true; no_data_consecutive++; } } else { - if(last_connected) - update_connected_specific_settings(&frontend_data, capture_data->status.device); - last_connected = false; default_sleep(); blank_out = true; } diff --git a/source/frontend.cpp b/source/frontend.cpp index 766e84d..9de8cd2 100755 --- a/source/frontend.cpp +++ b/source/frontend.cpp @@ -1,5 +1,6 @@ #include "frontend.hpp" #include +#include const CropData default_3ds_crop = { .top_width = TOP_WIDTH_3DS, .top_height = HEIGHT_3DS, @@ -796,9 +797,8 @@ void update_connected_specific_settings(FrontendData* frontend_data, const Captu void default_sleep(float wanted_ms) { if(wanted_ms < 0) - sf::sleep(sf::milliseconds(1000.0/USB_CHECKS_PER_SECOND)); - else - sf::sleep(sf::milliseconds(wanted_ms)); + wanted_ms = 1000.0/USB_CHECKS_PER_SECOND; + std::this_thread::sleep_for(std::chrono::microseconds((int)(wanted_ms * 1000))); } void screen_display_thread(WindowScreen *screen) {