Make camera selection persistent and add logging

This commit is contained in:
capitalistspz 2025-05-18 20:40:17 +01:00
parent d52800b7cd
commit fd49f0e334
7 changed files with 52 additions and 23 deletions

View file

@ -14,6 +14,7 @@ enum class LogType : sint32
UnsupportedAPI = 2, UnsupportedAPI = 2,
SoundAPI = 4, // any audio related API SoundAPI = 4, // any audio related API
InputAPI = 5, // any input related API InputAPI = 5, // any input related API
CameraAPI = 27,
Socket = 6, Socket = 6,
Save = 7, Save = 7,
H264 = 9, H264 = 9,

View file

@ -18,7 +18,6 @@ constexpr unsigned CAMERA_PITCH = 768;
namespace CameraManager namespace CameraManager
{ {
std::mutex s_mutex; std::mutex s_mutex;
bool s_initialized = false;
CapContext s_ctx; CapContext s_ctx;
std::optional<CapDeviceID> s_device; std::optional<CapDeviceID> s_device;
std::optional<CapStream> s_stream; std::optional<CapStream> s_stream;
@ -29,17 +28,38 @@ namespace CameraManager
std::atomic_bool s_capturing = false; std::atomic_bool s_capturing = false;
std::atomic_bool s_running = false; std::atomic_bool s_running = false;
std::string FourCC(uint32le value)
{
return {
static_cast<char>((value >> 0) & 0xFF),
static_cast<char>((value >> 8) & 0xFF),
static_cast<char>((value >> 16) & 0xFF),
static_cast<char>((value >> 24) & 0xFF)};
}
void CaptureLogFunction(uint32_t level, const char* string)
{
cemuLog_log(LogType::CameraAPI, "OpenPNPCapture: {}: {}", level, string);
}
std::optional<CapFormatID> FindCorrectFormat() std::optional<CapFormatID> FindCorrectFormat()
{ {
const auto formatCount = Cap_getNumFormats(s_ctx, *s_device); const auto device = *s_device;
cemuLog_log(LogType::CameraAPI, "Video capture device '{}' available formats:", Cap_getDeviceName(s_ctx, device));
const auto formatCount = Cap_getNumFormats(s_ctx, device);
for (int32_t formatId = 0; formatId < formatCount; ++formatId) for (int32_t formatId = 0; formatId < formatCount; ++formatId)
{ {
CapFormatInfo formatInfo; CapFormatInfo formatInfo;
if (Cap_getFormatInfo(s_ctx, *s_device, formatId, &formatInfo) != CAPRESULT_OK) if (Cap_getFormatInfo(s_ctx, device, formatId, &formatInfo) != CAPRESULT_OK)
continue; continue;
cemuLog_log(LogType::CameraAPI, "{}: {} {}x{} @ {} fps, {} bpp", formatId, FourCC(formatInfo.fourcc), formatInfo.width, formatInfo.height, formatInfo.fps, formatInfo.bpp);
if (formatInfo.width == CAMERA_WIDTH && formatInfo.height == CAMERA_HEIGHT) if (formatInfo.width == CAMERA_WIDTH && formatInfo.height == CAMERA_HEIGHT)
{
cemuLog_log(LogType::CameraAPI, "Selected video format {}", formatId);
return formatId; return formatId;
} }
}
cemuLog_log(LogType::CameraAPI, "Failed to find suitable video format");
return std::nullopt; return std::nullopt;
} }
@ -92,16 +112,16 @@ namespace CameraManager
{ {
{ {
std::scoped_lock lock(s_mutex); std::scoped_lock lock(s_mutex);
if (s_initialized) if (s_running)
return; return;
s_initialized = true;
s_running = true; s_running = true;
}
s_ctx = Cap_createContext(); s_ctx = Cap_createContext();
Cap_setLogLevel(4);
Cap_installCustomLogFunction(CaptureLogFunction);
}
s_captureThread = std::thread(&CaptureWorker); s_captureThread = std::thread(&CaptureWorker);
const auto uniqueId = GetConfig().camera_id.GetValue(); const auto uniqueId = GetConfig().camera_id.GetValue();
if (!uniqueId.empty()) if (!uniqueId.empty())
{ {
@ -121,8 +141,8 @@ namespace CameraManager
{ {
CloseStream(); CloseStream();
Cap_releaseContext(s_ctx); Cap_releaseContext(s_ctx);
s_running = false;
s_captureThread.join(); s_captureThread.join();
s_initialized = false;
} }
void FillNV12Buffer(uint8* nv12Buffer) void FillNV12Buffer(uint8* nv12Buffer)
{ {
@ -173,6 +193,7 @@ namespace CameraManager
std::scoped_lock lock(s_mutex); std::scoped_lock lock(s_mutex);
std::vector<DeviceInfo> infos; std::vector<DeviceInfo> infos;
const auto deviceCount = Cap_getDeviceCount(s_ctx); const auto deviceCount = Cap_getDeviceCount(s_ctx);
cemuLog_log(LogType::CameraAPI, "Available video capture devices:");
for (CapDeviceID deviceNo = 0; deviceNo < deviceCount; ++deviceNo) for (CapDeviceID deviceNo = 0; deviceNo < deviceCount; ++deviceNo)
{ {
const auto uniqueId = Cap_getDeviceUniqueID(s_ctx, deviceNo); const auto uniqueId = Cap_getDeviceUniqueID(s_ctx, deviceNo);
@ -181,19 +202,26 @@ namespace CameraManager
info.uniqueId = uniqueId; info.uniqueId = uniqueId;
if (name) if (name)
info.name = fmt::format("{}: {}", deviceNo + 1, name); info.name = fmt::format("{}: {}", deviceNo, name);
else else
info.name = fmt::format("{}: Unknown", deviceNo + 1); info.name = fmt::format("{}: Unknown", deviceNo);
infos.push_back(info); infos.push_back(info);
cemuLog_log(LogType::CameraAPI, "{}", info.name);
} }
if (infos.empty())
cemuLog_log(LogType::CameraAPI, "No available video capture devices");
return infos; return infos;
} }
void SaveDevice() void SaveDevice()
{ {
std::scoped_lock lock(s_mutex); std::scoped_lock lock(s_mutex);
if (s_device) auto& config = GetConfig();
GetConfig().camera_id = Cap_getDeviceUniqueID(s_ctx, *s_device); const auto cameraId = s_device ? Cap_getDeviceUniqueID(s_ctx, *s_device) : "";
else config.camera_id = cameraId;
GetConfig().camera_id = ""; }
std::optional<uint32> GetCurrentDevice()
{
return s_device;
} }
} // namespace CameraManager } // namespace CameraManager

View file

@ -21,4 +21,5 @@ namespace CameraManager
void SetDevice(uint32 deviceNo); void SetDevice(uint32 deviceNo);
std::vector<DeviceInfo> EnumerateDevices(); std::vector<DeviceInfo> EnumerateDevices();
void SaveDevice(); void SaveDevice();
std::optional<uint32> GetCurrentDevice();
} // namespace CameraManager } // namespace CameraManager

View file

@ -344,7 +344,7 @@ void CemuConfig::Load(XMLConfigParser& parser)
dsu_client.port = dsuc.get_attribute("port", dsu_client.port); dsu_client.port = dsuc.get_attribute("port", dsu_client.port);
auto camera = parser.get("Camera"); auto camera = parser.get("Camera");
camera_id = camera.get_attribute("Id", ""); camera_id = camera.get("Id", "");
// emulatedusbdevices // emulatedusbdevices
auto usbdevices = parser.get("EmulatedUsbDevices"); auto usbdevices = parser.get("EmulatedUsbDevices");

View file

@ -15,7 +15,8 @@ CameraSettingsWindow::CameraSettingsWindow(wxWindow* parent)
m_imageBitmap(CAMERA_WIDTH, CAMERA_HEIGHT, 24), m_imageBuffer(CAMERA_WIDTH * CAMERA_HEIGHT * 3) m_imageBitmap(CAMERA_WIDTH, CAMERA_HEIGHT, 24), m_imageBuffer(CAMERA_WIDTH * CAMERA_HEIGHT * 3)
{ {
CameraManager::Init();
CameraManager::Open();
auto* rootSizer = new wxBoxSizer(wxVERTICAL); auto* rootSizer = new wxBoxSizer(wxVERTICAL);
{ {
auto* topSizer = new wxBoxSizer(wxHORIZONTAL); auto* topSizer = new wxBoxSizer(wxHORIZONTAL);
@ -37,8 +38,6 @@ CameraSettingsWindow::CameraSettingsWindow(wxWindow* parent)
rootSizer->Add(m_imageWindow, wxEXPAND); rootSizer->Add(m_imageWindow, wxEXPAND);
} }
SetSizerAndFit(rootSizer); SetSizerAndFit(rootSizer);
CameraManager::Init();
CameraManager::Open();
m_imageUpdateTimer.Bind(wxEVT_TIMER, &CameraSettingsWindow::UpdateImage, this); m_imageUpdateTimer.Bind(wxEVT_TIMER, &CameraSettingsWindow::UpdateImage, this);
m_imageUpdateTimer.Start(33, wxTIMER_CONTINUOUS); m_imageUpdateTimer.Start(33, wxTIMER_CONTINUOUS);
this->Bind(wxEVT_CLOSE_WINDOW, &CameraSettingsWindow::OnClose, this); this->Bind(wxEVT_CLOSE_WINDOW, &CameraSettingsWindow::OnClose, this);
@ -61,8 +60,8 @@ void CameraSettingsWindow::OnRefreshPressed(wxCommandEvent&)
choices.push_back(entry.name); choices.push_back(entry.name);
} }
m_cameraChoice->Set(choices); m_cameraChoice->Set(choices);
wxArrayString str; if (auto currentDevice = CameraManager::GetCurrentDevice())
m_cameraChoice->SetSelection(*currentDevice + 1);
} }
void CameraSettingsWindow::UpdateImage(const wxTimerEvent&) void CameraSettingsWindow::UpdateImage(const wxTimerEvent&)
{ {

View file

@ -19,4 +19,3 @@ class CameraSettingsWindow : public wxDialog
void UpdateImage(const wxTimerEvent&); void UpdateImage(const wxTimerEvent&);
void OnClose(wxCloseEvent& event); void OnClose(wxCloseEvent& event);
}; };

View file

@ -2242,6 +2242,7 @@ void MainWindow::RecreateMenu()
logCosModulesMenu->AppendCheckItem(MAINFRAME_MENU_ID_DEBUG_LOGGING0 + stdx::to_underlying(LogType::GX2), _("gx2 API"), wxEmptyString)->Check(cemuLog_isLoggingEnabled(LogType::GX2)); logCosModulesMenu->AppendCheckItem(MAINFRAME_MENU_ID_DEBUG_LOGGING0 + stdx::to_underlying(LogType::GX2), _("gx2 API"), wxEmptyString)->Check(cemuLog_isLoggingEnabled(LogType::GX2));
logCosModulesMenu->AppendCheckItem(MAINFRAME_MENU_ID_DEBUG_LOGGING0 + stdx::to_underlying(LogType::SoundAPI), _("Audio API"), wxEmptyString)->Check(cemuLog_isLoggingEnabled(LogType::SoundAPI)); logCosModulesMenu->AppendCheckItem(MAINFRAME_MENU_ID_DEBUG_LOGGING0 + stdx::to_underlying(LogType::SoundAPI), _("Audio API"), wxEmptyString)->Check(cemuLog_isLoggingEnabled(LogType::SoundAPI));
logCosModulesMenu->AppendCheckItem(MAINFRAME_MENU_ID_DEBUG_LOGGING0 + stdx::to_underlying(LogType::InputAPI), _("Input API"), wxEmptyString)->Check(cemuLog_isLoggingEnabled(LogType::InputAPI)); logCosModulesMenu->AppendCheckItem(MAINFRAME_MENU_ID_DEBUG_LOGGING0 + stdx::to_underlying(LogType::InputAPI), _("Input API"), wxEmptyString)->Check(cemuLog_isLoggingEnabled(LogType::InputAPI));
logCosModulesMenu->AppendCheckItem(MAINFRAME_MENU_ID_DEBUG_LOGGING0 + stdx::to_underlying(LogType::CameraAPI), _("Camera API"), wxEmptyString)->Check(cemuLog_isLoggingEnabled(LogType::CameraAPI));
debugLoggingMenu->AppendSubMenu(logCosModulesMenu, _("&CafeOS modules logging")); debugLoggingMenu->AppendSubMenu(logCosModulesMenu, _("&CafeOS modules logging"));
debugLoggingMenu->AppendSeparator(); debugLoggingMenu->AppendSeparator();