mirror of
https://github.com/cemu-project/Cemu.git
synced 2025-04-29 14:59:26 -04:00
Change from singleton CameraManager
to namespace.
Update `openpnp-capture` dep for static linking. Fix camera selection dropdown width
This commit is contained in:
parent
29feae80e4
commit
d00222be11
7 changed files with 194 additions and 189 deletions
2
.gitmodules
vendored
2
.gitmodules
vendored
|
@ -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
|
||||
|
|
2
dependencies/openpnp-capture
vendored
2
dependencies/openpnp-capture
vendored
|
@ -1 +1 @@
|
|||
Subproject commit ba456ac7572c6e6db49da53d2f4985d65309e582
|
||||
Subproject commit 3daf77d3d4013238af4c97f0e3ca46c6d3666d1b
|
|
@ -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();
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -1,170 +1,195 @@
|
|||
#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;
|
||||
|
||||
const auto uniqueId = GetConfig().camera_id.GetValue();
|
||||
if (!uniqueId.empty())
|
||||
std::optional<CapFormatID> 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::DeviceInfo> CameraManager::EnumerateDevices()
|
||||
{
|
||||
std::scoped_lock lock(m_mutex);
|
||||
std::vector<DeviceInfo> 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<CapFormatID> 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;
|
||||
}
|
||||
}
|
||||
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(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
|
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue