diff --git a/.gitmodules b/.gitmodules index 55bf6afe..9ff86b40 100644 --- a/.gitmodules +++ b/.gitmodules @@ -21,4 +21,4 @@ [submodule "dependencies/openpnp-capture"] path = dependencies/openpnp-capture url = https://github.com/capitalistspz/openpnp-capture - branch = dev-no-explicit-shared + branch = dev diff --git a/dependencies/openpnp-capture b/dependencies/openpnp-capture index ba456ac7..3daf77d3 160000 --- a/dependencies/openpnp-capture +++ b/dependencies/openpnp-capture @@ -1 +1 @@ -Subproject commit ba456ac7572c6e6db49da53d2f4985d65309e582 +Subproject commit 3daf77d3d4013238af4c97f0e3ca46c6d3666d1b diff --git a/src/Cafe/OS/libs/camera/camera.cpp b/src/Cafe/OS/libs/camera/camera.cpp index 90f1b073..8c8e22da 100644 --- a/src/Cafe/OS/libs/camera/camera.cpp +++ b/src/Cafe/OS/libs/camera/camera.cpp @@ -146,7 +146,7 @@ namespace camera } else { - CameraManager::instance().FillNV12Buffer(surfaceBuffer.GetPtr()); + CameraManager::FillNV12Buffer(surfaceBuffer.GetPtr()); s_cameraEventData->data = surfaceBuffer; s_cameraEventData->errored = false; } @@ -189,6 +189,7 @@ namespace camera *error = CAM_STATUS_INVALID_ARG; return -1; } + CameraManager::Init(); cemu_assert_debug(initInfo->forceDisplay != CAMForceDisplay::DRC); cemu_assert_debug(initInfo->workMemorySize != 0); @@ -221,7 +222,7 @@ namespace camera return CAM_STATUS_UNINITIALIZED; s_instance.isOpen = false; } - CameraManager::instance().Close(); + CameraManager::Close(); return CAM_STATUS_SUCCESS; } @@ -234,7 +235,7 @@ namespace camera return CAM_STATUS_UNINITIALIZED; if (s_instance.isOpen) return CAM_STATUS_DEVICE_IN_USE; - CameraManager::instance().Open(); + CameraManager::Open(); s_instance.isOpen = true; coreinit::OSSignalEvent(s_cameraOpenEvent); s_instance.inTargetBuffers.Clear(); diff --git a/src/camera/CMakeLists.txt b/src/camera/CMakeLists.txt index 44342018..efe3f99f 100644 --- a/src/camera/CMakeLists.txt +++ b/src/camera/CMakeLists.txt @@ -8,7 +8,8 @@ add_library(CemuCamera set_property(TARGET CemuCamera PROPERTY MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>") target_include_directories(CemuCamera PUBLIC "../") -target_link_libraries(CemuCamera PRIVATE CemuCommon CemuUtil PUBLIC openpnp-capture) +target_link_libraries(CemuCamera PRIVATE CemuCommon CemuUtil openpnp-capture) + if (ENABLE_WXWIDGETS) target_link_libraries(CemuCamera PRIVATE wx::base) diff --git a/src/camera/CameraManager.cpp b/src/camera/CameraManager.cpp index 88bbf993..33698ff0 100644 --- a/src/camera/CameraManager.cpp +++ b/src/camera/CameraManager.cpp @@ -1,170 +1,195 @@ #include "CameraManager.h" -#include "Rgb2Nv12.h" + #include "config/CemuConfig.h" #include "util/helpers/helpers.h" +#include "Rgb2Nv12.h" + +#include +#include +#include +#include + +#include constexpr unsigned CAMERA_WIDTH = 640; constexpr unsigned CAMERA_HEIGHT = 480; constexpr unsigned CAMERA_PITCH = 768; -CameraManager::CameraManager() - : m_ctx(Cap_createContext()), - m_rgbBuffer(CAMERA_WIDTH * CAMERA_HEIGHT * 3), - m_nv12Buffer(CAMERA_PITCH * CAMERA_HEIGHT * 3 / 2), - m_refCount(0), m_capturing(false), m_running(true) +namespace CameraManager { - m_captureThread = std::thread(&CameraManager::CaptureWorker, this); + std::mutex s_mutex; + bool s_initialized = false; + CapContext s_ctx; + std::optional s_device; + std::optional s_stream; + std::array s_rgbBuffer; + std::array s_nv12Buffer; + int s_refCount = 0; + std::thread s_captureThread; + std::atomic_bool s_capturing = false; + std::atomic_bool s_running = false; - const auto uniqueId = GetConfig().camera_id.GetValue(); - if (!uniqueId.empty()) + std::optional FindCorrectFormat() { - const auto devices = EnumerateDevices(); - for (CapDeviceID deviceId = 0; deviceId < devices.size(); ++deviceId) + const auto formatCount = Cap_getNumFormats(s_ctx, *s_device); + for (int32_t formatId = 0; formatId < formatCount; ++formatId) { - if (devices[deviceId].uniqueId == uniqueId) + CapFormatInfo formatInfo; + if (Cap_getFormatInfo(s_ctx, *s_device, formatId, &formatInfo) != CAPRESULT_OK) + continue; + if (formatInfo.width == CAMERA_WIDTH && formatInfo.height == CAMERA_HEIGHT) + return formatId; + } + return std::nullopt; + } + + void CaptureWorker() + { + SetThreadName("CameraManager"); + while (s_running) + { + while (s_capturing) { - m_device = deviceId; - return; + s_mutex.lock(); + if (s_stream && Cap_hasNewFrame(s_ctx, *s_stream) && + Cap_captureFrame(s_ctx, *s_stream, s_rgbBuffer.data(), s_rgbBuffer.size()) == CAPRESULT_OK) + Rgb2Nv12(s_rgbBuffer.data(), CAMERA_WIDTH, CAMERA_HEIGHT, s_nv12Buffer.data(), CAMERA_PITCH); + s_mutex.unlock(); + std::this_thread::sleep_for(std::chrono::milliseconds(30)); + } + std::this_thread::sleep_for(std::chrono::seconds(1)); + std::this_thread::yield(); + } + } + void OpenStream() + { + const auto formatId = FindCorrectFormat(); + if (!formatId) + return; + const auto stream = Cap_openStream(s_ctx, *s_device, *formatId); + if (stream == -1) + return; + s_capturing = true; + s_stream = stream; + } + void CloseStream() + { + s_capturing = false; + if (s_stream) + { + Cap_closeStream(s_ctx, *s_stream); + s_stream = std::nullopt; + } + } + void ResetBuffers() + { + std::ranges::fill(s_rgbBuffer, 0); + std::ranges::fill_n(s_nv12Buffer.begin(), CAMERA_WIDTH * CAMERA_PITCH, 16); + std::ranges::fill_n(s_nv12Buffer.begin() + CAMERA_WIDTH * CAMERA_PITCH, (CAMERA_WIDTH / 2), 128); + } + + void Init() + { + std::scoped_lock lock(s_mutex); + if (s_initialized) + return; + s_mutex.unlock(); + s_running = true; + s_captureThread = std::thread(&CaptureWorker); + s_ctx = Cap_createContext(); + + const auto uniqueId = GetConfig().camera_id.GetValue(); + if (!uniqueId.empty()) + { + const auto devices = EnumerateDevices(); + for (CapDeviceID deviceId = 0; deviceId < devices.size(); ++deviceId) + { + if (devices[deviceId].uniqueId == uniqueId) + { + s_device = deviceId; + return; + } } } - } - ResetBuffers(); -} -CameraManager::~CameraManager() -{ - m_running = false; - CloseStream(); - Cap_releaseContext(m_ctx); -} - -void CameraManager::SetDevice(uint32 deviceNo) -{ - std::scoped_lock lock(m_mutex); - CloseStream(); - if (deviceNo == DEVICE_NONE) - { - m_device = std::nullopt; ResetBuffers(); - return; } - m_device = deviceNo; - if (m_refCount != 0) - OpenStream(); -} -std::vector CameraManager::EnumerateDevices() -{ - std::scoped_lock lock(m_mutex); - std::vector infos; - const auto deviceCount = Cap_getDeviceCount(m_ctx); - for (CapDeviceID deviceNo = 0; deviceNo < deviceCount; ++deviceNo) + void Deinit() { - const auto uniqueId = Cap_getDeviceUniqueID(m_ctx, deviceNo); - const auto name = Cap_getDeviceName(m_ctx, deviceNo); - DeviceInfo info; - info.uniqueId = uniqueId; - - if (name) - info.name = fmt::format("{}: {}", deviceNo + 1, name); - else - info.name = fmt::format("{}: Unknown", deviceNo + 1); - infos.push_back(info); + CloseStream(); + Cap_releaseContext(s_ctx); + s_captureThread.join(); + s_initialized = false; } - return infos; -} -void CameraManager::SaveDevice() -{ - std::scoped_lock lock(m_mutex); - if (m_device) - GetConfig().camera_id = Cap_getDeviceUniqueID(m_ctx, *m_device); - else - GetConfig().camera_id = ""; -} -void CameraManager::Open() -{ - std::scoped_lock lock(m_mutex); - if (m_device && m_refCount == 0) + void FillNV12Buffer(uint8* nv12Buffer) { - OpenStream(); + std::scoped_lock lock(s_mutex); + std::ranges::copy(s_nv12Buffer, nv12Buffer); } - m_refCount += 1; -} -void CameraManager::Close() -{ - std::scoped_lock lock(m_mutex); - if (m_refCount == 0) - return; - m_refCount -= 1; - if (m_refCount != 0) - return; - CloseStream(); -} -std::optional CameraManager::FindCorrectFormat() -{ - const auto formatCount = Cap_getNumFormats(m_ctx, *m_device); - for (CapFormatID formatId = 0; formatId < formatCount; ++formatId) + void FillRGBBuffer(uint8* rgbBuffer) { - CapFormatInfo formatInfo; - if (Cap_getFormatInfo(m_ctx, *m_device, formatId, &formatInfo) != CAPRESULT_OK) - continue; - if (formatInfo.width == CAMERA_WIDTH && formatInfo.height == CAMERA_HEIGHT) - return formatId; + std::scoped_lock lock(s_mutex); + std::ranges::copy(s_rgbBuffer, rgbBuffer); } - return std::nullopt; -} -void CameraManager::ResetBuffers() -{ - std::ranges::fill(m_rgbBuffer, 0); - std::ranges::fill_n(m_nv12Buffer.begin(), CAMERA_WIDTH * CAMERA_PITCH, 16); - std::ranges::fill_n(m_nv12Buffer.begin() + CAMERA_WIDTH * CAMERA_PITCH, (CAMERA_WIDTH / 2), 128); -} - -void CameraManager::FillNV12Buffer(uint8* nv12Buffer) const -{ - std::scoped_lock lock(m_mutex); - std::ranges::copy(m_nv12Buffer, nv12Buffer); -} - -void CameraManager::FillRGBBuffer(uint8* rgbBuffer) const -{ - std::scoped_lock lock(m_mutex); - std::ranges::copy(m_rgbBuffer, rgbBuffer); -} -void CameraManager::CaptureWorker() -{ - SetThreadName("CameraManager"); - while (m_running) + void SetDevice(uint32 deviceNo) { - while (m_capturing) + std::scoped_lock lock(s_mutex); + CloseStream(); + if (deviceNo == DEVICE_NONE) { - m_mutex.lock(); - if (m_stream && Cap_hasNewFrame(m_ctx, *m_stream) && - Cap_captureFrame(m_ctx, *m_stream, m_rgbBuffer.data(), m_rgbBuffer.size()) == CAPRESULT_OK) - Rgb2Nv12(m_rgbBuffer.data(), CAMERA_WIDTH, CAMERA_HEIGHT, m_nv12Buffer.data(), CAMERA_PITCH); - m_mutex.unlock(); - std::this_thread::sleep_for(std::chrono::milliseconds(30)); + s_device = std::nullopt; + ResetBuffers(); + return; } - std::this_thread::sleep_for(std::chrono::seconds(1)); - std::this_thread::yield(); + s_device = deviceNo; + if (s_refCount != 0) + OpenStream(); } -} -void CameraManager::OpenStream() -{ - const auto formatId = FindCorrectFormat(); - if (!formatId) - return; - const auto stream = Cap_openStream(m_ctx, *m_device, *formatId); - if (stream == -1) - return; - m_capturing = true; - m_stream = stream; -} -void CameraManager::CloseStream() -{ - m_capturing = false; - if (m_stream) + void Open() { - Cap_closeStream(m_ctx, *m_stream); - m_stream = std::nullopt; + std::scoped_lock lock(s_mutex); + if (s_device && s_refCount == 0) + { + OpenStream(); + } + s_refCount += 1; } -} \ No newline at end of file + void Close() + { + std::scoped_lock lock(s_mutex); + if (s_refCount == 0) + return; + s_refCount -= 1; + if (s_refCount != 0) + return; + CloseStream(); + } + std::vector EnumerateDevices() + { + std::scoped_lock lock(s_mutex); + std::vector infos; + const auto deviceCount = Cap_getDeviceCount(s_ctx); + for (CapDeviceID deviceNo = 0; deviceNo < deviceCount; ++deviceNo) + { + const auto uniqueId = Cap_getDeviceUniqueID(s_ctx, deviceNo); + const auto name = Cap_getDeviceName(s_ctx, deviceNo); + DeviceInfo info; + info.uniqueId = uniqueId; + + if (name) + info.name = fmt::format("{}: {}", deviceNo + 1, name); + else + info.name = fmt::format("{}: Unknown", deviceNo + 1); + infos.push_back(info); + } + return infos; + } + void SaveDevice() + { + std::scoped_lock lock(s_mutex); + if (s_device) + GetConfig().camera_id = Cap_getDeviceUniqueID(s_ctx, *s_device); + else + GetConfig().camera_id = ""; + } +} // namespace CameraManager \ No newline at end of file diff --git a/src/camera/CameraManager.h b/src/camera/CameraManager.h index e8e0c971..f372e86c 100644 --- a/src/camera/CameraManager.h +++ b/src/camera/CameraManager.h @@ -1,46 +1,24 @@ #pragma once -#include -#include -#include -#include -#include "util/helpers/Singleton.h" +#include -class CameraManager : public Singleton +namespace CameraManager { - CapContext m_ctx; - std::optional m_device; - std::optional m_stream; - std::vector m_rgbBuffer; - std::vector m_nv12Buffer; - int m_refCount; - std::thread m_captureThread; - std::atomic_bool m_capturing; - std::atomic_bool m_running; - mutable std::recursive_mutex m_mutex; - - public: - constexpr static uint32 DEVICE_NONE = std::numeric_limits::max(); struct DeviceInfo { std::string uniqueId; std::string name; }; - CameraManager(); - ~CameraManager(); + constexpr static uint32 DEVICE_NONE = std::numeric_limits::max(); + + void Init(); + void Deinit(); + void Open(); + void Close(); + + void FillNV12Buffer(uint8* nv12Buffer); + void FillRGBBuffer(uint8* rgbBuffer); void SetDevice(uint32 deviceNo); std::vector EnumerateDevices(); void SaveDevice(); - - void Open(); - void Close(); - void FillNV12Buffer(uint8* nv12Buffer) const; - void FillRGBBuffer(uint8* rgbBuffer) const; - - private: - std::optional FindCorrectFormat(); - void ResetBuffers(); - void CaptureWorker(); - void OpenStream(); - void CloseStream(); -}; +} // namespace CameraManager diff --git a/src/gui/CameraSettingsWindow.cpp b/src/gui/CameraSettingsWindow.cpp index e73d9211..4014513f 100644 --- a/src/gui/CameraSettingsWindow.cpp +++ b/src/gui/CameraSettingsWindow.cpp @@ -20,8 +20,7 @@ CameraSettingsWindow::CameraSettingsWindow(wxWindow* parent) { auto* topSizer = new wxBoxSizer(wxHORIZONTAL); { - wxString choices[] = {_("None")}; - m_cameraChoice = new wxChoice(this, wxID_ANY, wxDefaultPosition, {300, -1}, 1, choices); + m_cameraChoice = new wxChoice(this, wxID_ANY, wxDefaultPosition, {300, -1}); m_cameraChoice->Bind(wxEVT_CHOICE, &CameraSettingsWindow::OnSelectCameraChoice, this); m_refreshButton = new wxButton(this, wxID_ANY, wxString::FromUTF8("⟳")); @@ -38,7 +37,8 @@ CameraSettingsWindow::CameraSettingsWindow(wxWindow* parent) rootSizer->Add(m_imageWindow, wxEXPAND); } SetSizerAndFit(rootSizer); - CameraManager::instance().Open(); + CameraManager::Init(); + CameraManager::Open(); m_imageUpdateTimer.Bind(wxEVT_TIMER, &CameraSettingsWindow::UpdateImage, this); m_imageUpdateTimer.Start(33, wxTIMER_CONTINUOUS); this->Bind(wxEVT_CLOSE_WINDOW, &CameraSettingsWindow::OnClose, this); @@ -49,14 +49,14 @@ void CameraSettingsWindow::OnSelectCameraChoice(wxCommandEvent&) if (selection < 0) return; if (selection == 0) - CameraManager::instance().SetDevice(CameraManager::DEVICE_NONE); + CameraManager::SetDevice(CameraManager::DEVICE_NONE); else - CameraManager::instance().SetDevice(selection - 1); + CameraManager::SetDevice(selection - 1); } void CameraSettingsWindow::OnRefreshPressed(wxCommandEvent&) { wxArrayString choices = {_("None")}; - for (const auto& entry : CameraManager::instance().EnumerateDevices()) + for (const auto& entry : CameraManager::EnumerateDevices()) { choices.push_back(entry.name); } @@ -66,7 +66,7 @@ void CameraSettingsWindow::OnRefreshPressed(wxCommandEvent&) } void CameraSettingsWindow::UpdateImage(const wxTimerEvent&) { - CameraManager::instance().FillRGBBuffer(m_imageBuffer.data()); + CameraManager::FillRGBBuffer(m_imageBuffer.data()); wxNativePixelData data{m_imageBitmap}; if (!data) @@ -92,7 +92,7 @@ void CameraSettingsWindow::UpdateImage(const wxTimerEvent&) } void CameraSettingsWindow::OnClose(wxCloseEvent& event) { - CameraManager::instance().Close(); - CameraManager::instance().SaveDevice(); + CameraManager::Close(); + CameraManager::SaveDevice(); event.Skip(); }