qt: Migrate to Qt6. (#6418)

This commit is contained in:
Steveice10 2023-05-05 03:10:34 -07:00 committed by GitHub
parent 70335a7f4d
commit 2273df4d70
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
32 changed files with 299 additions and 464 deletions

View file

@ -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

View file

@ -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