mirror of
https://github.com/cemu-project/Cemu.git
synced 2025-07-03 21:41:19 +12:00
Make camera selection persistent and add logging
This commit is contained in:
parent
d52800b7cd
commit
fd49f0e334
7 changed files with 52 additions and 23 deletions
|
@ -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,
|
||||||
|
|
|
@ -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
|
|
@ -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
|
||||||
|
|
|
@ -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");
|
||||||
|
|
|
@ -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&)
|
||||||
{
|
{
|
||||||
|
|
|
@ -19,4 +19,3 @@ class CameraSettingsWindow : public wxDialog
|
||||||
void UpdateImage(const wxTimerEvent&);
|
void UpdateImage(const wxTimerEvent&);
|
||||||
void OnClose(wxCloseEvent& event);
|
void OnClose(wxCloseEvent& event);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -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();
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue