mirror of
https://github.com/PabloMK7/citra.git
synced 2025-09-11 13:20:04 +00:00
qt: Migrate to Qt6. (#6418)
This commit is contained in:
parent
70335a7f4d
commit
2273df4d70
32 changed files with 299 additions and 464 deletions
|
@ -3,9 +3,8 @@
|
|||
// Refer to the license.txt file included.
|
||||
|
||||
#include <QCamera>
|
||||
#include <QCameraInfo>
|
||||
#include <QImageReader>
|
||||
#include <QMessageBox>
|
||||
#include <QMediaDevices>
|
||||
#include <QThread>
|
||||
#include "citra_qt/camera/qt_multimedia_camera.h"
|
||||
#include "citra_qt/main.h"
|
||||
|
@ -16,229 +15,85 @@
|
|||
|
||||
namespace Camera {
|
||||
|
||||
QList<QVideoFrame::PixelFormat> QtCameraSurface::supportedPixelFormats(
|
||||
[[maybe_unused]] QAbstractVideoBuffer::HandleType handleType) const {
|
||||
return QList<QVideoFrame::PixelFormat>()
|
||||
<< QVideoFrame::Format_RGB32 << QVideoFrame::Format_RGB24
|
||||
<< QVideoFrame::Format_ARGB32_Premultiplied << QVideoFrame::Format_ARGB32
|
||||
<< QVideoFrame::Format_RGB565 << QVideoFrame::Format_RGB555
|
||||
<< QVideoFrame::Format_Jpeg
|
||||
// the following formats are supported via Qt internal conversions
|
||||
<< QVideoFrame::Format_ARGB8565_Premultiplied << QVideoFrame::Format_BGRA32
|
||||
<< QVideoFrame::Format_BGRA32_Premultiplied << QVideoFrame::Format_BGR32
|
||||
<< QVideoFrame::Format_BGR24 << QVideoFrame::Format_BGR565 << QVideoFrame::Format_BGR555
|
||||
<< QVideoFrame::Format_AYUV444 << QVideoFrame::Format_YUV444
|
||||
<< QVideoFrame::Format_YUV420P << QVideoFrame::Format_YV12 << QVideoFrame::Format_UYVY
|
||||
<< QVideoFrame::Format_YUYV << QVideoFrame::Format_NV12
|
||||
<< QVideoFrame::Format_NV21; // Supporting all the QImage convertible formats, ordered by
|
||||
// QImage decoding performance
|
||||
}
|
||||
|
||||
bool QtCameraSurface::present(const QVideoFrame& frame) {
|
||||
if (!frame.isValid()) {
|
||||
return false;
|
||||
}
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)
|
||||
QMutexLocker locker(&mutex);
|
||||
// In Qt 5.15, the image is already flipped
|
||||
current_frame = frame.image();
|
||||
locker.unlock();
|
||||
#else
|
||||
QVideoFrame cloneFrame(frame);
|
||||
cloneFrame.map(QAbstractVideoBuffer::ReadOnly);
|
||||
const QImage image(cloneFrame.bits(), cloneFrame.width(), cloneFrame.height(),
|
||||
QVideoFrame::imageFormatFromPixelFormat(cloneFrame.pixelFormat()));
|
||||
QMutexLocker locker(&mutex);
|
||||
current_frame = image.mirrored(true, true);
|
||||
locker.unlock();
|
||||
cloneFrame.unmap();
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
QtMultimediaCamera::QtMultimediaCamera(const std::string& camera_name,
|
||||
const Service::CAM::Flip& flip)
|
||||
: QtCameraInterface(flip), handler(QtMultimediaCameraHandler::GetHandler(camera_name)) {
|
||||
if (handler->thread() == QThread::currentThread()) {
|
||||
handler->CreateCamera(camera_name);
|
||||
} else {
|
||||
QMetaObject::invokeMethod(handler.get(), "CreateCamera", Qt::BlockingQueuedConnection,
|
||||
Q_ARG(const std::string&, camera_name));
|
||||
}
|
||||
}
|
||||
|
||||
QtMultimediaCamera::~QtMultimediaCamera() {
|
||||
handler->StopCamera();
|
||||
QtMultimediaCameraHandler::ReleaseHandler(handler);
|
||||
}
|
||||
|
||||
void QtMultimediaCamera::StartCapture() {
|
||||
if (handler->thread() == QThread::currentThread()) {
|
||||
handler->StartCamera();
|
||||
} else {
|
||||
QMetaObject::invokeMethod(handler.get(), "StartCamera", Qt::BlockingQueuedConnection);
|
||||
}
|
||||
}
|
||||
|
||||
void QtMultimediaCamera::StopCapture() {
|
||||
handler->StopCamera();
|
||||
}
|
||||
|
||||
void QtMultimediaCamera::SetFrameRate(Service::CAM::FrameRate frame_rate) {
|
||||
const std::array<QCamera::FrameRateRange, 13> FrameRateList = {
|
||||
/* Rate_15 */ QCamera::FrameRateRange(15, 15),
|
||||
/* Rate_15_To_5 */ QCamera::FrameRateRange(5, 15),
|
||||
/* Rate_15_To_2 */ QCamera::FrameRateRange(2, 15),
|
||||
/* Rate_10 */ QCamera::FrameRateRange(10, 10),
|
||||
/* Rate_8_5 */ QCamera::FrameRateRange(8.5, 8.5),
|
||||
/* Rate_5 */ QCamera::FrameRateRange(5, 5),
|
||||
/* Rate_20 */ QCamera::FrameRateRange(20, 20),
|
||||
/* Rate_20_To_5 */ QCamera::FrameRateRange(5, 20),
|
||||
/* Rate_30 */ QCamera::FrameRateRange(30, 30),
|
||||
/* Rate_30_To_5 */ QCamera::FrameRateRange(5, 30),
|
||||
/* Rate_15_To_10 */ QCamera::FrameRateRange(10, 15),
|
||||
/* Rate_20_To_10 */ QCamera::FrameRateRange(10, 20),
|
||||
/* Rate_30_To_10 */ QCamera::FrameRateRange(10, 30),
|
||||
};
|
||||
|
||||
auto framerate = FrameRateList[static_cast<int>(frame_rate)];
|
||||
|
||||
if (handler->camera->supportedViewfinderFrameRateRanges().contains(framerate)) {
|
||||
handler->settings.setMinimumFrameRate(framerate.minimumFrameRate);
|
||||
handler->settings.setMaximumFrameRate(framerate.maximumFrameRate);
|
||||
}
|
||||
}
|
||||
|
||||
QImage QtMultimediaCamera::QtReceiveFrame() {
|
||||
QMutexLocker locker(&handler->camera_surface.mutex);
|
||||
return handler->camera_surface.current_frame;
|
||||
}
|
||||
|
||||
bool QtMultimediaCamera::IsPreviewAvailable() {
|
||||
return handler->CameraAvailable();
|
||||
}
|
||||
|
||||
std::unique_ptr<CameraInterface> QtMultimediaCameraFactory::Create(const std::string& config,
|
||||
const Service::CAM::Flip& flip) {
|
||||
return std::make_unique<QtMultimediaCamera>(config, flip);
|
||||
}
|
||||
|
||||
std::array<std::shared_ptr<QtMultimediaCameraHandler>, 3> QtMultimediaCameraHandler::handlers;
|
||||
|
||||
std::array<bool, 3> QtMultimediaCameraHandler::status;
|
||||
|
||||
std::unordered_map<std::string, std::shared_ptr<QtMultimediaCameraHandler>>
|
||||
QtMultimediaCameraHandler::loaded;
|
||||
|
||||
void QtMultimediaCameraHandler::Init() {
|
||||
std::generate(std::begin(handlers), std::end(handlers),
|
||||
std::make_shared<QtMultimediaCameraHandler>);
|
||||
}
|
||||
|
||||
std::shared_ptr<QtMultimediaCameraHandler> QtMultimediaCameraHandler::GetHandler(
|
||||
std::shared_ptr<QtMultimediaCameraHandler> QtMultimediaCameraHandlerFactory::Create(
|
||||
const std::string& camera_name) {
|
||||
if (loaded.count(camera_name)) {
|
||||
return loaded.at(camera_name);
|
||||
}
|
||||
for (std::size_t i = 0; i < handlers.size(); i++) {
|
||||
if (!status[i]) {
|
||||
LOG_INFO(Service_CAM, "Successfully got handler {}", i);
|
||||
status[i] = true;
|
||||
loaded.emplace(camera_name, handlers[i]);
|
||||
return handlers[i];
|
||||
if (thread() == QThread::currentThread()) {
|
||||
std::shared_ptr<QtMultimediaCameraHandler> handler;
|
||||
if (!handlers.contains(camera_name) || !(handler = handlers[camera_name].lock())) {
|
||||
LOG_INFO(Service_CAM, "Creating new handler for camera '{}'", camera_name);
|
||||
handler = std::make_shared<QtMultimediaCameraHandler>(camera_name);
|
||||
handlers[camera_name] = handler;
|
||||
} else {
|
||||
LOG_INFO(Service_CAM, "Reusing existing handler for camera '{}'", camera_name);
|
||||
}
|
||||
return handler;
|
||||
} else {
|
||||
std::shared_ptr<QtMultimediaCameraHandler> handler;
|
||||
QMetaObject::invokeMethod(this, "Create", Qt::BlockingQueuedConnection,
|
||||
Q_RETURN_ARG(std::shared_ptr<QtMultimediaCameraHandler>, handler),
|
||||
Q_ARG(std::string, camera_name));
|
||||
return handler;
|
||||
}
|
||||
LOG_CRITICAL(Service_CAM, "All handlers taken up");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void QtMultimediaCameraHandler::ReleaseHandler(
|
||||
const std::shared_ptr<Camera::QtMultimediaCameraHandler>& handler) {
|
||||
for (std::size_t i = 0; i < handlers.size(); i++) {
|
||||
if (handlers[i] == handler) {
|
||||
LOG_INFO(Service_CAM, "Successfully released handler {}", i);
|
||||
status[i] = false;
|
||||
handlers[i]->started = false;
|
||||
for (auto it = loaded.begin(); it != loaded.end(); it++) {
|
||||
if (it->second == handlers[i]) {
|
||||
loaded.erase(it);
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
void QtMultimediaCameraHandlerFactory::PauseCameras() {
|
||||
LOG_INFO(Service_CAM, "Pausing all cameras");
|
||||
for (auto& handler_pair : handlers) {
|
||||
auto handler = handler_pair.second.lock();
|
||||
if (handler && handler->IsActive()) {
|
||||
handler->PauseCapture();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void QtMultimediaCameraHandler::CreateCamera(const std::string& camera_name) {
|
||||
QList<QCameraInfo> cameras = QCameraInfo::availableCameras();
|
||||
for (const QCameraInfo& cameraInfo : cameras) {
|
||||
if (cameraInfo.deviceName().toStdString() == camera_name)
|
||||
camera = std::make_unique<QCamera>(cameraInfo);
|
||||
void QtMultimediaCameraHandlerFactory::ResumeCameras() {
|
||||
LOG_INFO(Service_CAM, "Resuming all cameras");
|
||||
for (auto& handler_pair : handlers) {
|
||||
auto handler = handler_pair.second.lock();
|
||||
if (handler && handler->IsPaused()) {
|
||||
handler->StartCapture();
|
||||
}
|
||||
}
|
||||
if (!camera) { // no cameras found, using default camera
|
||||
}
|
||||
|
||||
QtMultimediaCameraHandler::QtMultimediaCameraHandler(const std::string& camera_name) {
|
||||
auto cameras = QMediaDevices::videoInputs();
|
||||
auto requested_camera =
|
||||
std::find_if(cameras.begin(), cameras.end(), [camera_name](QCameraDevice& camera_info) {
|
||||
return camera_info.description().toStdString() == camera_name;
|
||||
});
|
||||
if (requested_camera != cameras.end()) {
|
||||
camera = std::make_unique<QCamera>(*requested_camera);
|
||||
} else {
|
||||
camera = std::make_unique<QCamera>();
|
||||
}
|
||||
settings.setMinimumFrameRate(30);
|
||||
settings.setMaximumFrameRate(30);
|
||||
camera->setViewfinder(&camera_surface);
|
||||
camera->load();
|
||||
if (camera->supportedViewfinderPixelFormats().isEmpty()) {
|
||||
// The gstreamer plugin (used on linux systems) returns an empty list on querying supported
|
||||
// viewfinder pixel formats, and will not work without expliciting setting it to some value,
|
||||
// so we are defaulting to RGB565 here which should be fairly widely supported.
|
||||
settings.setPixelFormat(QVideoFrame::PixelFormat::Format_RGB565);
|
||||
}
|
||||
camera_surface = std::make_unique<QVideoSink>();
|
||||
capture_session.setVideoSink(camera_surface.get());
|
||||
capture_session.setCamera(camera.get());
|
||||
}
|
||||
|
||||
void QtMultimediaCameraHandler::StopCamera() {
|
||||
camera->stop();
|
||||
started = false;
|
||||
QtMultimediaCameraHandler::~QtMultimediaCameraHandler() {
|
||||
StopCapture();
|
||||
}
|
||||
|
||||
void QtMultimediaCameraHandler::StartCamera() {
|
||||
void QtMultimediaCameraHandler::StartCapture() {
|
||||
if (!camera->isActive()) {
|
||||
#if defined(__APPLE__)
|
||||
if (!AppleAuthorization::CheckAuthorizationForCamera()) {
|
||||
LOG_ERROR(Service_CAM, "Unable to start camera due to lack of authorization");
|
||||
return;
|
||||
}
|
||||
if (!AppleAuthorization::CheckAuthorizationForCamera()) {
|
||||
LOG_ERROR(Service_CAM, "Unable to start camera due to lack of authorization");
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
camera->setViewfinderSettings(settings);
|
||||
camera->start();
|
||||
started = true;
|
||||
camera->start();
|
||||
}
|
||||
paused = false;
|
||||
}
|
||||
|
||||
bool QtMultimediaCameraHandler::CameraAvailable() const {
|
||||
return camera && camera->isAvailable();
|
||||
}
|
||||
|
||||
void QtMultimediaCameraHandler::StopCameras() {
|
||||
LOG_INFO(Service_CAM, "Stopping all cameras");
|
||||
for (auto& handler : handlers) {
|
||||
if (handler && handler->started) {
|
||||
handler->StopCamera();
|
||||
handler->paused = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void QtMultimediaCameraHandler::ResumeCameras() {
|
||||
for (auto& handler : handlers) {
|
||||
if (handler && handler->paused) {
|
||||
handler->StartCamera();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void QtMultimediaCameraHandler::ReleaseHandlers() {
|
||||
StopCameras();
|
||||
LOG_INFO(Service_CAM, "Releasing all handlers");
|
||||
for (std::size_t i = 0; i < handlers.size(); i++) {
|
||||
status[i] = false;
|
||||
handlers[i]->started = false;
|
||||
handlers[i]->paused = false;
|
||||
void QtMultimediaCameraHandler::StopCapture() {
|
||||
if (camera->isActive()) {
|
||||
camera->stop();
|
||||
}
|
||||
paused = false;
|
||||
}
|
||||
|
||||
} // namespace Camera
|
||||
|
|
|
@ -4,99 +4,113 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
#include <QAbstractVideoSurface>
|
||||
#include <QCamera>
|
||||
#include <QCameraViewfinderSettings>
|
||||
#include <QImage>
|
||||
#include <QMutex>
|
||||
#include <QMediaCaptureSession>
|
||||
#include <QVideoSink>
|
||||
#include "citra_qt/camera/camera_util.h"
|
||||
#include "citra_qt/camera/qt_camera_base.h"
|
||||
#include "core/frontend/camera/interface.h"
|
||||
|
||||
class GMainWindow;
|
||||
|
||||
namespace Camera {
|
||||
|
||||
class QtCameraSurface final : public QAbstractVideoSurface {
|
||||
// NOTE: Must be created on the Qt thread. QtMultimediaCameraHandlerFactory ensures this.
|
||||
class QtMultimediaCameraHandler final : public QObject {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
QList<QVideoFrame::PixelFormat> supportedPixelFormats(
|
||||
QAbstractVideoBuffer::HandleType) const override;
|
||||
bool present(const QVideoFrame&) override;
|
||||
explicit QtMultimediaCameraHandler(const std::string& camera_name);
|
||||
~QtMultimediaCameraHandler();
|
||||
|
||||
void StartCapture();
|
||||
void StopCapture();
|
||||
|
||||
QImage QtReceiveFrame() {
|
||||
return camera_surface->videoFrame().toImage();
|
||||
}
|
||||
|
||||
bool IsPreviewAvailable() {
|
||||
return camera->isAvailable();
|
||||
}
|
||||
|
||||
bool IsActive() {
|
||||
return camera->isActive();
|
||||
}
|
||||
|
||||
[[nodiscard]] bool IsPaused() {
|
||||
return paused;
|
||||
}
|
||||
|
||||
void PauseCapture() {
|
||||
StopCapture();
|
||||
paused = true;
|
||||
}
|
||||
|
||||
private:
|
||||
QMutex mutex;
|
||||
QImage current_frame;
|
||||
|
||||
friend class QtMultimediaCamera; // For access to current_frame
|
||||
std::unique_ptr<QCamera> camera;
|
||||
std::unique_ptr<QVideoSink> camera_surface;
|
||||
QMediaCaptureSession capture_session{};
|
||||
bool paused = false; // was previously started but was paused, to be resumed
|
||||
};
|
||||
|
||||
class QtMultimediaCameraHandler;
|
||||
// NOTE: Must be created on the Qt thread.
|
||||
class QtMultimediaCameraHandlerFactory final : public QObject {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
Q_INVOKABLE std::shared_ptr<QtMultimediaCameraHandler> Create(const std::string& camera_name);
|
||||
void PauseCameras();
|
||||
void ResumeCameras();
|
||||
|
||||
private:
|
||||
std::unordered_map<std::string, std::weak_ptr<QtMultimediaCameraHandler>> handlers;
|
||||
};
|
||||
|
||||
/// This class is only an interface. It just calls QtMultimediaCameraHandler.
|
||||
class QtMultimediaCamera final : public QtCameraInterface {
|
||||
public:
|
||||
QtMultimediaCamera(const std::string& camera_name, const Service::CAM::Flip& flip);
|
||||
~QtMultimediaCamera();
|
||||
void StartCapture() override;
|
||||
void StopCapture() override;
|
||||
void SetFrameRate(Service::CAM::FrameRate frame_rate) override;
|
||||
QImage QtReceiveFrame() override;
|
||||
bool IsPreviewAvailable() override;
|
||||
QtMultimediaCamera(const std::shared_ptr<QtMultimediaCameraHandler>& handler,
|
||||
const Service::CAM::Flip& flip)
|
||||
: QtCameraInterface(flip), handler(handler) {}
|
||||
|
||||
void StartCapture() override {
|
||||
handler->StartCapture();
|
||||
}
|
||||
|
||||
void StopCapture() override {
|
||||
handler->StopCapture();
|
||||
}
|
||||
|
||||
void SetFrameRate(Service::CAM::FrameRate frame_rate) override {}
|
||||
|
||||
QImage QtReceiveFrame() override {
|
||||
return handler->QtReceiveFrame();
|
||||
}
|
||||
|
||||
bool IsPreviewAvailable() override {
|
||||
return handler->IsPreviewAvailable();
|
||||
}
|
||||
|
||||
private:
|
||||
std::shared_ptr<QtMultimediaCameraHandler> handler;
|
||||
};
|
||||
|
||||
/// This class is only an interface. It just calls QtMultimediaCameraHandlerFactory.
|
||||
class QtMultimediaCameraFactory final : public QtCameraFactory {
|
||||
public:
|
||||
QtMultimediaCameraFactory(
|
||||
const std::shared_ptr<QtMultimediaCameraHandlerFactory>& handler_factory)
|
||||
: handler_factory(handler_factory) {}
|
||||
|
||||
std::unique_ptr<CameraInterface> Create(const std::string& config,
|
||||
const Service::CAM::Flip& flip) override;
|
||||
};
|
||||
|
||||
class QtMultimediaCameraHandler final : public QObject {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
/// Creates the global handler. Must be called in UI thread.
|
||||
static void Init();
|
||||
static std::shared_ptr<QtMultimediaCameraHandler> GetHandler(const std::string& camera_name);
|
||||
static void ReleaseHandler(const std::shared_ptr<QtMultimediaCameraHandler>& handler);
|
||||
|
||||
/**
|
||||
* Creates the camera.
|
||||
* Note: This function must be called via QMetaObject::invokeMethod in UI thread.
|
||||
*/
|
||||
Q_INVOKABLE void CreateCamera(const std::string& camera_name);
|
||||
|
||||
/**
|
||||
* Starts the camera.
|
||||
* Note: This function must be called via QMetaObject::invokeMethod in UI thread when
|
||||
* starting the camera for the first time. 'Resume' calls can be in other threads.
|
||||
*/
|
||||
Q_INVOKABLE void StartCamera();
|
||||
|
||||
void StopCamera();
|
||||
bool CameraAvailable() const;
|
||||
static void StopCameras();
|
||||
static void ResumeCameras();
|
||||
static void ReleaseHandlers();
|
||||
const Service::CAM::Flip& flip) override {
|
||||
return std::make_unique<QtMultimediaCamera>(handler_factory->Create(config), flip);
|
||||
}
|
||||
|
||||
private:
|
||||
std::unique_ptr<QCamera> camera;
|
||||
QtCameraSurface camera_surface{};
|
||||
QCameraViewfinderSettings settings;
|
||||
bool started = false;
|
||||
bool paused = false; // was previously started but was paused, to be resumed
|
||||
|
||||
static std::array<std::shared_ptr<QtMultimediaCameraHandler>, 3> handlers;
|
||||
static std::array<bool, 3> status;
|
||||
static std::unordered_map<std::string, std::shared_ptr<QtMultimediaCameraHandler>> loaded;
|
||||
|
||||
friend class QtMultimediaCamera; // For access to camera_surface (and camera)
|
||||
std::shared_ptr<QtMultimediaCameraHandlerFactory> handler_factory;
|
||||
};
|
||||
|
||||
} // namespace Camera
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue