Change from singleton CameraManager to namespace.

Update `openpnp-capture` dep for static linking.
Fix camera selection dropdown width
This commit is contained in:
capitalistspz 2025-03-31 10:04:25 +01:00
parent 29feae80e4
commit d00222be11
7 changed files with 194 additions and 189 deletions

2
.gitmodules vendored
View file

@ -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

@ -1 +1 @@
Subproject commit ba456ac7572c6e6db49da53d2f4985d65309e582
Subproject commit 3daf77d3d4013238af4c97f0e3ca46c6d3666d1b

View file

@ -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();

View file

@ -8,7 +8,8 @@ add_library(CemuCamera
set_property(TARGET CemuCamera PROPERTY MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>: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)

View file

@ -1,19 +1,102 @@
#include "CameraManager.h"
#include "Rgb2Nv12.h"
#include "config/CemuConfig.h"
#include "util/helpers/helpers.h"
#include "Rgb2Nv12.h"
#include <algorithm>
#include <mutex>
#include <optional>
#include <thread>
#include <openpnp-capture.h>
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<CapDeviceID> s_device;
std::optional<CapStream> s_stream;
std::array<uint8, CAMERA_WIDTH * CAMERA_HEIGHT * 3> s_rgbBuffer;
std::array<uint8, CAMERA_PITCH * CAMERA_HEIGHT * 3 / 2> s_nv12Buffer;
int s_refCount = 0;
std::thread s_captureThread;
std::atomic_bool s_capturing = false;
std::atomic_bool s_running = false;
std::optional<CapFormatID> FindCorrectFormat()
{
const auto formatCount = Cap_getNumFormats(s_ctx, *s_device);
for (int32_t formatId = 0; formatId < formatCount; ++formatId)
{
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)
{
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())
@ -23,43 +106,73 @@ CameraManager::CameraManager()
{
if (devices[deviceId].uniqueId == uniqueId)
{
m_device = deviceId;
s_device = deviceId;
return;
}
}
}
ResetBuffers();
}
CameraManager::~CameraManager()
{
m_running = false;
}
void Deinit()
{
CloseStream();
Cap_releaseContext(m_ctx);
}
Cap_releaseContext(s_ctx);
s_captureThread.join();
s_initialized = false;
}
void FillNV12Buffer(uint8* nv12Buffer)
{
std::scoped_lock lock(s_mutex);
std::ranges::copy(s_nv12Buffer, nv12Buffer);
}
void CameraManager::SetDevice(uint32 deviceNo)
{
std::scoped_lock lock(m_mutex);
void FillRGBBuffer(uint8* rgbBuffer)
{
std::scoped_lock lock(s_mutex);
std::ranges::copy(s_rgbBuffer, rgbBuffer);
}
void SetDevice(uint32 deviceNo)
{
std::scoped_lock lock(s_mutex);
CloseStream();
if (deviceNo == DEVICE_NONE)
{
m_device = std::nullopt;
s_device = std::nullopt;
ResetBuffers();
return;
}
m_device = deviceNo;
if (m_refCount != 0)
s_device = deviceNo;
if (s_refCount != 0)
OpenStream();
}
std::vector<CameraManager::DeviceInfo> CameraManager::EnumerateDevices()
{
std::scoped_lock lock(m_mutex);
}
void Open()
{
std::scoped_lock lock(s_mutex);
if (s_device && s_refCount == 0)
{
OpenStream();
}
s_refCount += 1;
}
void Close()
{
std::scoped_lock lock(s_mutex);
if (s_refCount == 0)
return;
s_refCount -= 1;
if (s_refCount != 0)
return;
CloseStream();
}
std::vector<DeviceInfo> EnumerateDevices()
{
std::scoped_lock lock(s_mutex);
std::vector<DeviceInfo> infos;
const auto deviceCount = Cap_getDeviceCount(m_ctx);
const auto deviceCount = Cap_getDeviceCount(s_ctx);
for (CapDeviceID deviceNo = 0; deviceNo < deviceCount; ++deviceNo)
{
const auto uniqueId = Cap_getDeviceUniqueID(m_ctx, deviceNo);
const auto name = Cap_getDeviceName(m_ctx, deviceNo);
const auto uniqueId = Cap_getDeviceUniqueID(s_ctx, deviceNo);
const auto name = Cap_getDeviceName(s_ctx, deviceNo);
DeviceInfo info;
info.uniqueId = uniqueId;
@ -70,101 +183,13 @@ std::vector<CameraManager::DeviceInfo> CameraManager::EnumerateDevices()
infos.push_back(info);
}
return infos;
}
void CameraManager::SaveDevice()
{
std::scoped_lock lock(m_mutex);
if (m_device)
GetConfig().camera_id = Cap_getDeviceUniqueID(m_ctx, *m_device);
}
void SaveDevice()
{
std::scoped_lock lock(s_mutex);
if (s_device)
GetConfig().camera_id = Cap_getDeviceUniqueID(s_ctx, *s_device);
else
GetConfig().camera_id = "";
}
void CameraManager::Open()
{
std::scoped_lock lock(m_mutex);
if (m_device && m_refCount == 0)
{
OpenStream();
}
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<CapFormatID> CameraManager::FindCorrectFormat()
{
const auto formatCount = Cap_getNumFormats(m_ctx, *m_device);
for (CapFormatID formatId = 0; formatId < formatCount; ++formatId)
{
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;
}
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)
{
while (m_capturing)
{
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));
}
std::this_thread::sleep_for(std::chrono::seconds(1));
std::this_thread::yield();
}
}
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)
{
Cap_closeStream(m_ctx, *m_stream);
m_stream = std::nullopt;
}
}
} // namespace CameraManager

View file

@ -1,46 +1,24 @@
#pragma once
#include <atomic>
#include <mutex>
#include <thread>
#include <openpnp-capture.h>
#include "util/helpers/Singleton.h"
#include <string>
class CameraManager : public Singleton<CameraManager>
namespace CameraManager
{
CapContext m_ctx;
std::optional<CapDeviceID> m_device;
std::optional<CapStream> m_stream;
std::vector<uint8> m_rgbBuffer;
std::vector<uint8> 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<uint32>::max();
struct DeviceInfo
{
std::string uniqueId;
std::string name;
};
CameraManager();
~CameraManager();
constexpr static uint32 DEVICE_NONE = std::numeric_limits<uint32>::max();
void Init();
void Deinit();
void Open();
void Close();
void FillNV12Buffer(uint8* nv12Buffer);
void FillRGBBuffer(uint8* rgbBuffer);
void SetDevice(uint32 deviceNo);
std::vector<DeviceInfo> EnumerateDevices();
void SaveDevice();
void Open();
void Close();
void FillNV12Buffer(uint8* nv12Buffer) const;
void FillRGBBuffer(uint8* rgbBuffer) const;
private:
std::optional<CapFormatID> FindCorrectFormat();
void ResetBuffers();
void CaptureWorker();
void OpenStream();
void CloseStream();
};
} // namespace CameraManager

View file

@ -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();
}