Browse Source

[desktop] port IR Camera to Qt6 & fix camera saving on windows

pull/3630/head
smiRaphi 1 week ago
committed by crueter
parent
commit
ba9e2da6c8
  1. 14
      CMakeLists.txt
  2. 8
      src/yuzu/CMakeLists.txt
  3. 57
      src/yuzu/bootmanager.cpp
  4. 10
      src/yuzu/bootmanager.h
  5. 80
      src/yuzu/configuration/configure_camera.cpp
  6. 11
      src/yuzu/configuration/configure_camera.h
  7. 4
      src/yuzu/configuration/configure_input_advanced.cpp

14
CMakeLists.txt

@ -70,7 +70,7 @@ endif()
option(ENABLE_QT "Enable the Qt frontend" ON) option(ENABLE_QT "Enable the Qt frontend" ON)
option(ENABLE_QT_TRANSLATION "Enable translations for the Qt frontend" OFF) option(ENABLE_QT_TRANSLATION "Enable translations for the Qt frontend" OFF)
option(ENABLE_UPDATE_CHECKER "Enable update checker (for Qt and Android)" OFF) option(ENABLE_UPDATE_CHECKER "Enable update checker (for Qt and Android)" OFF)
# option(YUZU_USE_QT_MULTIMEDIA "Use QtMultimedia for Camera" OFF)
cmake_dependent_option(YUZU_USE_QT_MULTIMEDIA "Use QtMultimedia for Camera" OFF "NOT YUZU_USE_BUNDLED_QT" OFF)
cmake_dependent_option(YUZU_USE_QT_WEB_ENGINE "Use QtWebEngine for web applet implementation" OFF "NOT YUZU_USE_BUNDLED_QT" OFF) cmake_dependent_option(YUZU_USE_QT_WEB_ENGINE "Use QtWebEngine for web applet implementation" OFF "NOT YUZU_USE_BUNDLED_QT" OFF)
set(YUZU_QT_MIRROR "" CACHE STRING "What mirror to use for downloading the bundled Qt libraries") set(YUZU_QT_MIRROR "" CACHE STRING "What mirror to use for downloading the bundled Qt libraries")
cmake_dependent_option(YUZU_USE_BUNDLED_QT "Download bundled Qt binaries" "${MSVC}" "ENABLE_QT" OFF) cmake_dependent_option(YUZU_USE_BUNDLED_QT "Download bundled Qt binaries" "${MSVC}" "ENABLE_QT" OFF)
@ -577,9 +577,9 @@ if (ENABLE_QT)
find_package(Qt6 CONFIG REQUIRED COMPONENTS Widgets Charts Concurrent) find_package(Qt6 CONFIG REQUIRED COMPONENTS Widgets Charts Concurrent)
# if (YUZU_USE_QT_MULTIMEDIA)
# find_package(Qt6 REQUIRED COMPONENTS Multimedia)
# endif()
if (YUZU_USE_QT_MULTIMEDIA)
find_package(Qt6 REQUIRED COMPONENTS Multimedia)
endif()
if (PLATFORM_LINUX OR PLATFORM_FREEBSD) if (PLATFORM_LINUX OR PLATFORM_FREEBSD)
# yes Qt, we get it # yes Qt, we get it
@ -618,9 +618,9 @@ if (ENABLE_QT)
if (PLATFORM_LINUX) if (PLATFORM_LINUX)
list(APPEND YUZU_QT_COMPONENTS DBus) list(APPEND YUZU_QT_COMPONENTS DBus)
endif() endif()
# if (YUZU_USE_QT_MULTIMEDIA)
# list(APPEND YUZU_QT_COMPONENTS Multimedia)
# endif()
if (YUZU_USE_QT_MULTIMEDIA)
list(APPEND YUZU_QT_COMPONENTS Multimedia)
endif()
if (YUZU_USE_QT_WEB_ENGINE) if (YUZU_USE_QT_WEB_ENGINE)
list(APPEND YUZU_QT_COMPONENTS WebEngineCore WebEngineWidgets) list(APPEND YUZU_QT_COMPONENTS WebEngineCore WebEngineWidgets)
endif() endif()

8
src/yuzu/CMakeLists.txt

@ -412,10 +412,10 @@ if (ENABLE_WEB_SERVICE)
target_compile_definitions(yuzu PRIVATE ENABLE_WEB_SERVICE) target_compile_definitions(yuzu PRIVATE ENABLE_WEB_SERVICE)
endif() endif()
# if (YUZU_USE_QT_MULTIMEDIA)
# target_link_libraries(yuzu PRIVATE Qt6::Multimedia)
# target_compile_definitions(yuzu PRIVATE YUZU_USE_QT_MULTIMEDIA)
# endif ()
if (YUZU_USE_QT_MULTIMEDIA)
target_link_libraries(yuzu PRIVATE Qt6::Multimedia)
target_compile_definitions(yuzu PRIVATE YUZU_USE_QT_MULTIMEDIA)
endif ()
if (YUZU_USE_QT_WEB_ENGINE) if (YUZU_USE_QT_WEB_ENGINE)
target_link_libraries(yuzu PRIVATE Qt6::WebEngineCore Qt6::WebEngineWidgets) target_link_libraries(yuzu PRIVATE Qt6::WebEngineCore Qt6::WebEngineWidgets)

57
src/yuzu/bootmanager.cpp

@ -13,10 +13,11 @@
#include <QtCore/qglobal.h> #include <QtCore/qglobal.h>
#include "common/settings_enums.h" #include "common/settings_enums.h"
#include "qt_common/config/uisettings.h" #include "qt_common/config/uisettings.h"
#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) && YUZU_USE_QT_MULTIMEDIA
#if YUZU_USE_QT_MULTIMEDIA
#include <QCamera> #include <QCamera>
#include <QCameraImageCapture>
#include <QCameraInfo>
#include <QImageCapture>
#include <QMediaCaptureSession>
#include <QMediaDevices>
#endif #endif
#include <QCursor> #include <QCursor>
#include <QEvent> #include <QEvent>
@ -756,24 +757,25 @@ void GRenderWindow::TouchEndEvent() {
} }
void GRenderWindow::InitializeCamera() { void GRenderWindow::InitializeCamera() {
#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) && YUZU_USE_QT_MULTIMEDIA
#if YUZU_USE_QT_MULTIMEDIA
constexpr auto camera_update_ms = std::chrono::milliseconds{50}; // (50ms, 20Hz) constexpr auto camera_update_ms = std::chrono::milliseconds{50}; // (50ms, 20Hz)
if (!Settings::values.enable_ir_sensor) { if (!Settings::values.enable_ir_sensor) {
return; return;
} }
bool camera_found = false; bool camera_found = false;
const QList<QCameraInfo> cameras = QCameraInfo::availableCameras();
for (const QCameraInfo& cameraInfo : cameras) {
if (Settings::values.ir_sensor_device.GetValue() == cameraInfo.deviceName().toStdString() ||
Settings::values.ir_sensor_device.GetValue() == "Auto") {
camera = std::make_unique<QCamera>(cameraInfo);
if (!camera->isCaptureModeSupported(QCamera::CaptureMode::CaptureViewfinder) &&
!camera->isCaptureModeSupported(QCamera::CaptureMode::CaptureStillImage)) {
LOG_ERROR(Frontend,
"Camera doesn't support CaptureViewfinder or CaptureStillImage");
std::string current_device = Settings::values.ir_sensor_device.GetValue();
#ifdef _WIN32
std::replace(current_device.begin(), current_device.end(), '|', '\\');
#endif
const QList<QCameraDevice> cameras = QMediaDevices::videoInputs();
for (const QCameraDevice& cameraDevice : cameras) {
if (current_device == cameraDevice.id().toStdString() || current_device == "auto") {
if (cameraDevice.videoFormats().isEmpty()) {
LOG_ERROR(Frontend, "Camera doesn't provide any video formats.");
continue; continue;
} }
camera = std::make_unique<QCamera>(cameraDevice);
camera_found = true; camera_found = true;
break; break;
} }
@ -783,27 +785,16 @@ void GRenderWindow::InitializeCamera() {
return; return;
} }
camera_capture = std::make_unique<QCameraImageCapture>(camera.get());
if (!camera_capture->isCaptureDestinationSupported(
QCameraImageCapture::CaptureDestination::CaptureToBuffer)) {
LOG_ERROR(Frontend, "Camera doesn't support saving to buffer");
return;
}
capture_session = std::make_unique<QMediaCaptureSession>();
camera_capture = std::make_unique<QImageCapture>();
capture_session->setCamera(camera.get());
capture_session->setImageCapture(camera_capture.get());
const auto camera_width = input_subsystem->GetCamera()->getImageWidth(); const auto camera_width = input_subsystem->GetCamera()->getImageWidth();
const auto camera_height = input_subsystem->GetCamera()->getImageHeight(); const auto camera_height = input_subsystem->GetCamera()->getImageHeight();
camera_data.resize(camera_width * camera_height); camera_data.resize(camera_width * camera_height);
camera_capture->setCaptureDestination(QCameraImageCapture::CaptureDestination::CaptureToBuffer);
connect(camera_capture.get(), &QCameraImageCapture::imageCaptured, this,
connect(camera_capture.get(), &QImageCapture::imageCaptured, this,
&GRenderWindow::OnCameraCapture); &GRenderWindow::OnCameraCapture);
camera->unload();
if (camera->isCaptureModeSupported(QCamera::CaptureMode::CaptureViewfinder)) {
camera->setCaptureMode(QCamera::CaptureViewfinder);
} else if (camera->isCaptureModeSupported(QCamera::CaptureMode::CaptureStillImage)) {
camera->setCaptureMode(QCamera::CaptureStillImage);
}
camera->load();
camera->start(); camera->start();
pending_camera_snapshots = 0; pending_camera_snapshots = 0;
@ -817,18 +808,18 @@ void GRenderWindow::InitializeCamera() {
} }
void GRenderWindow::FinalizeCamera() { void GRenderWindow::FinalizeCamera() {
#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) && YUZU_USE_QT_MULTIMEDIA
#if YUZU_USE_QT_MULTIMEDIA
if (camera_timer) { if (camera_timer) {
camera_timer->stop(); camera_timer->stop();
} }
if (camera) { if (camera) {
camera->unload();
camera->stop();
} }
#endif #endif
} }
void GRenderWindow::RequestCameraCapture() { void GRenderWindow::RequestCameraCapture() {
#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) && YUZU_USE_QT_MULTIMEDIA
#if YUZU_USE_QT_MULTIMEDIA
if (!Settings::values.enable_ir_sensor) { if (!Settings::values.enable_ir_sensor) {
return; return;
} }
@ -849,7 +840,7 @@ void GRenderWindow::RequestCameraCapture() {
} }
void GRenderWindow::OnCameraCapture(int requestId, const QImage& img) { void GRenderWindow::OnCameraCapture(int requestId, const QImage& img) {
#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) && YUZU_USE_QT_MULTIMEDIA
#if YUZU_USE_QT_MULTIMEDIA
// TODO: Capture directly in the format and resolution needed // TODO: Capture directly in the format and resolution needed
const auto camera_width = input_subsystem->GetCamera()->getImageWidth(); const auto camera_width = input_subsystem->GetCamera()->getImageWidth();
const auto camera_height = input_subsystem->GetCamera()->getImageHeight(); const auto camera_height = input_subsystem->GetCamera()->getImageHeight();

10
src/yuzu/bootmanager.h

@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: 2014 Citra Emulator Project // SPDX-FileCopyrightText: 2014 Citra Emulator Project
@ -34,7 +34,8 @@
class MainWindow; class MainWindow;
class QCamera; class QCamera;
class QCameraImageCapture;
class QImageCapture;
class QMediaCaptureSession;
class QCloseEvent; class QCloseEvent;
class QFocusEvent; class QFocusEvent;
class QKeyEvent; class QKeyEvent;
@ -264,12 +265,13 @@ private:
bool first_frame = false; bool first_frame = false;
InputCommon::TasInput::TasState last_tas_state; InputCommon::TasInput::TasState last_tas_state;
#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) && YUZU_USE_QT_MULTIMEDIA
#if YUZU_USE_QT_MULTIMEDIA
bool is_virtual_camera; bool is_virtual_camera;
int pending_camera_snapshots; int pending_camera_snapshots;
std::vector<u32> camera_data; std::vector<u32> camera_data;
std::unique_ptr<QCamera> camera; std::unique_ptr<QCamera> camera;
std::unique_ptr<QCameraImageCapture> camera_capture;
std::unique_ptr<QImageCapture> camera_capture;
std::unique_ptr<QMediaCaptureSession> capture_session;
std::unique_ptr<QTimer> camera_timer; std::unique_ptr<QTimer> camera_timer;
#endif #endif

80
src/yuzu/configuration/configure_camera.cpp

@ -1,11 +1,16 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// Text : Copyright 2022 yuzu Emulator Project // Text : Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-License-Identifier: GPL-3.0-or-later
#include <memory> #include <memory>
#include <QtCore> #include <QtCore>
#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) && YUZU_USE_QT_MULTIMEDIA
#include <QCameraImageCapture>
#include <QCameraInfo>
#if YUZU_USE_QT_MULTIMEDIA
#include <QCamera>
#include <QImageCapture>
#include <QMediaCaptureSession>
#include <QMediaDevices>
#endif #endif
#include <QStandardItemModel> #include <QStandardItemModel>
#include <QTimer> #include <QTimer>
@ -36,22 +41,20 @@ ConfigureCamera::ConfigureCamera(QWidget* parent, InputCommon::InputSubsystem* i
ConfigureCamera::~ConfigureCamera() = default; ConfigureCamera::~ConfigureCamera() = default;
void ConfigureCamera::PreviewCamera() { void ConfigureCamera::PreviewCamera() {
#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) && YUZU_USE_QT_MULTIMEDIA
#if YUZU_USE_QT_MULTIMEDIA
const auto index = ui->ir_sensor_combo_box->currentIndex(); const auto index = ui->ir_sensor_combo_box->currentIndex();
bool camera_found = false; bool camera_found = false;
const QList<QCameraInfo> cameras = QCameraInfo::availableCameras();
for (const QCameraInfo& cameraInfo : cameras) {
if (input_devices[index] == cameraInfo.deviceName().toStdString() ||
input_devices[index] == "Auto") {
LOG_INFO(Frontend, "Selected Camera {} {}", cameraInfo.description().toStdString(),
cameraInfo.deviceName().toStdString());
camera = std::make_unique<QCamera>(cameraInfo);
if (!camera->isCaptureModeSupported(QCamera::CaptureMode::CaptureViewfinder) &&
!camera->isCaptureModeSupported(QCamera::CaptureMode::CaptureStillImage)) {
LOG_ERROR(Frontend,
"Camera doesn't support CaptureViewfinder or CaptureStillImage");
const QList<QCameraDevice> cameras = QMediaDevices::videoInputs();
for (const QCameraDevice& cameraDevice : cameras) {
if (input_devices[index] == cameraDevice.id().toStdString() ||
input_devices[index] == "auto") {
LOG_INFO(Frontend, "Selected Camera {} {}", cameraDevice.description().toStdString(),
cameraDevice.id().toStdString());
if (cameraDevice.videoFormats().isEmpty()) {
LOG_ERROR(Frontend, "Camera doesn't provide any video formats.");
continue; continue;
} }
camera = std::make_unique<QCamera>(cameraDevice);
camera_found = true; camera_found = true;
break; break;
} }
@ -66,24 +69,12 @@ void ConfigureCamera::PreviewCamera() {
return; return;
} }
camera_capture = std::make_unique<QCameraImageCapture>(camera.get());
if (!camera_capture->isCaptureDestinationSupported(
QCameraImageCapture::CaptureDestination::CaptureToBuffer)) {
LOG_ERROR(Frontend, "Camera doesn't support saving to buffer");
return;
}
camera_capture->setCaptureDestination(QCameraImageCapture::CaptureDestination::CaptureToBuffer);
connect(camera_capture.get(), &QCameraImageCapture::imageCaptured, this,
capture_session = std::make_unique<QMediaCaptureSession>();
camera_capture = std::make_unique<QImageCapture>();
capture_session->setCamera(camera.get());
capture_session->setImageCapture(camera_capture.get());
connect(camera_capture.get(), &QImageCapture::imageCaptured, this,
&ConfigureCamera::DisplayCapturedFrame); &ConfigureCamera::DisplayCapturedFrame);
camera->unload();
if (camera->isCaptureModeSupported(QCamera::CaptureMode::CaptureViewfinder)) {
camera->setCaptureMode(QCamera::CaptureViewfinder);
} else if (camera->isCaptureModeSupported(QCamera::CaptureMode::CaptureStillImage)) {
camera->setCaptureMode(QCamera::CaptureStillImage);
}
camera->load();
camera->start(); camera->start();
pending_snapshots = 0; pending_snapshots = 0;
@ -129,24 +120,31 @@ void ConfigureCamera::RetranslateUI() {
} }
void ConfigureCamera::ApplyConfiguration() { void ConfigureCamera::ApplyConfiguration() {
const auto index = ui->ir_sensor_combo_box->currentIndex();
Settings::values.ir_sensor_device.SetValue(input_devices[index]);
std::string current_device = input_devices[ui->ir_sensor_combo_box->currentIndex()];
#ifdef _WIN32
// for whatever reason replacing with / isn't enough so we use | for saving
std::replace(current_device.begin(), current_device.end(), '\\', '|');
#endif
Settings::values.ir_sensor_device.SetValue(current_device);
} }
void ConfigureCamera::LoadConfiguration() { void ConfigureCamera::LoadConfiguration() {
input_devices.clear(); input_devices.clear();
ui->ir_sensor_combo_box->clear(); ui->ir_sensor_combo_box->clear();
input_devices.push_back("Auto");
input_devices.push_back("auto");
ui->ir_sensor_combo_box->addItem(tr("Auto")); ui->ir_sensor_combo_box->addItem(tr("Auto"));
#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) && YUZU_USE_QT_MULTIMEDIA
const auto cameras = QCameraInfo::availableCameras();
for (const QCameraInfo& cameraInfo : cameras) {
input_devices.push_back(cameraInfo.deviceName().toStdString());
ui->ir_sensor_combo_box->addItem(cameraInfo.description());
#if YUZU_USE_QT_MULTIMEDIA
const auto cameras = QMediaDevices::videoInputs();
for (const QCameraDevice& cameraDevice : cameras) {
input_devices.push_back(cameraDevice.id().toStdString());
ui->ir_sensor_combo_box->addItem(cameraDevice.description());
} }
#endif #endif
const auto current_device = Settings::values.ir_sensor_device.GetValue();
std::string current_device = Settings::values.ir_sensor_device.GetValue();
#ifdef _WIN32
std::replace(current_device.begin(), current_device.end(), '|', '\\');
#endif
const auto devices_it = std::find_if( const auto devices_it = std::find_if(
input_devices.begin(), input_devices.end(), input_devices.begin(), input_devices.end(),

11
src/yuzu/configuration/configure_camera.h

@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// Text : Copyright 2022 yuzu Emulator Project // Text : Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-License-Identifier: GPL-3.0-or-later
@ -8,7 +11,8 @@
class QTimer; class QTimer;
class QCamera; class QCamera;
class QCameraImageCapture;
class QImageCapture;
class QMediaCaptureSession;
namespace InputCommon { namespace InputCommon {
class InputSubsystem; class InputSubsystem;
@ -46,9 +50,10 @@ private:
bool is_virtual_camera; bool is_virtual_camera;
int pending_snapshots; int pending_snapshots;
#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) && YUZU_USE_QT_MULTIMEDIA
#if YUZU_USE_QT_MULTIMEDIA
std::unique_ptr<QCamera> camera; std::unique_ptr<QCamera> camera;
std::unique_ptr<QCameraImageCapture> camera_capture;
std::unique_ptr<QImageCapture> camera_capture;
std::unique_ptr<QMediaCaptureSession> capture_session;
#endif #endif
std::unique_ptr<QTimer> camera_timer; std::unique_ptr<QTimer> camera_timer;
std::vector<std::string> input_devices; std::vector<std::string> input_devices;

4
src/yuzu/configuration/configure_input_advanced.cpp

@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
@ -201,7 +201,7 @@ void ConfigureInputAdvanced::UpdateUIEnabled() {
ui->debug_configure->setEnabled(ui->debug_enabled->isChecked()); ui->debug_configure->setEnabled(ui->debug_enabled->isChecked());
ui->touchscreen_advanced->setEnabled(ui->touchscreen_enabled->isChecked()); ui->touchscreen_advanced->setEnabled(ui->touchscreen_enabled->isChecked());
ui->ring_controller_configure->setEnabled(ui->enable_ring_controller->isChecked()); ui->ring_controller_configure->setEnabled(ui->enable_ring_controller->isChecked());
#if QT_VERSION > QT_VERSION_CHECK(6, 0, 0) || !defined(YUZU_USE_QT_MULTIMEDIA)
#if !defined(YUZU_USE_QT_MULTIMEDIA)
ui->enable_ir_sensor->setEnabled(false); ui->enable_ir_sensor->setEnabled(false);
ui->camera_configure->setEnabled(false); ui->camera_configure->setEnabled(false);
#endif #endif

Loading…
Cancel
Save