citra/src/citra_qt/configuration/configure_camera.cpp
朱鹏飞 57827de38b citra_qt: camera integration (#3566)
* Implement camera feature

* Make OpenCVCamera optional

* Fix styling problems

* CI configuration

* Fix CI

* Hopefully final fix

* Hopefully final fix

* Fix all the problems

* Oops..

* Add Qt Multimedia Camera

* Another oops

* Try to fix broken linux CI

* Try to fix broken linux CI

* Fix problems

* Improve UI

* Fix problems

* camera: Add support for Qt <5.10 and fix preview error

* CI: try to fix linux-frozen travis build

* camera: fix problems and add multiple handlers support

* fix CI

* remove most ServiceFramework changes

* Fix format

* Remove last ServiceFramework change

* camera: remove unused interfaces; revert submodule change

* camera: fix CI error

* ci: use ccache for opencv build

* citra_qt: fix configuration error; CI: add mediaservice plugin

* citra_qt: fix clang-format

* citra_qt: fix documentation error

* citra_qt: fix configuration page; camera: fix pausing crash

* citra_qt: fix preview not stopping

* camera: extend handlers length

* camera: fix camera resume error

* camera: fix clang-format

* camera: remove all OpenCV; citra_qt: rewrite configuration

* camera: remove all OpenCV; citra_qt: rewrite configuration

* camera: remove all OpenCV; citra_qt: rewrite configuration

* CI: fix linux ci

* camera: check settings update; citra_qt: fix configuration error

* service_cam: use a better way to apply configuration

* Service_CAM: rewrite camera reload

* cam: fix clang format

* citra_qt: fix argument load issue; camera: base of system camera selection

* citra_qt: Add system camera selection

* camera: fix image upside down, Implement SetFrameRate in Qt Multimedia Camera

* camera: Add missing <array> include, update SetFrameRate and add settings in Qt Multimedia Camera header

* camera: move started in Qt Multimedia Camera header

* QtMultimediaCamera: Move frame rates to SetFrameRate; Set minimum and maximum frame rate

* Update appveyor.yml
2018-05-11 11:42:23 -06:00

324 lines
12 KiB
C++

// Copyright 2018 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <memory>
#include <QCameraInfo>
#include <QDirIterator>
#include <QFileDialog>
#include <QImageReader>
#include <QMessageBox>
#include <QWidget>
#include "citra_qt/configuration/configure_camera.h"
#include "citra_qt/ui_settings.h"
#include "core/core.h"
#include "core/settings.h"
#include "ui_configure_camera.h"
const std::array<std::string, 3> ConfigureCamera::Implementations = {
"blank", /* Blank */
"image", /* Image */
"qt" /* System Camera */
};
ConfigureCamera::ConfigureCamera(QWidget* parent)
: QWidget(parent), ui(std::make_unique<Ui::ConfigureCamera>()) {
ui->setupUi(this);
// Load settings
camera_name = Settings::values.camera_name;
camera_config = Settings::values.camera_config;
for (auto&& item : camera_name) {
if (item == "opencv") {
QMessageBox::critical(this, tr("Error"),
tr("Sorry, Citra has removed support for OpenCV cameras.\n\nYour "
"existing OpenCV cameras have been replaced with Blank."));
item = "blank";
}
}
QList<QCameraInfo> cameras = QCameraInfo::availableCameras();
for (const QCameraInfo& cameraInfo : cameras) {
ui->system_camera->addItem(cameraInfo.deviceName());
}
updateCameraMode();
setConfiguration();
connectEvents();
ui->preview_box->setHidden(true);
}
ConfigureCamera::~ConfigureCamera() {
stopPreviewing();
}
void ConfigureCamera::connectEvents() {
connect(ui->image_source,
static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this, [this] {
stopPreviewing();
updateImageSourceUI();
});
connect(ui->camera_selection,
static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this, [this] {
stopPreviewing();
if (getCameraSelection() != current_selected) {
recordConfig();
}
if (ui->camera_selection->currentIndex() == 1) {
ui->camera_mode->setCurrentIndex(1); // Double
if (camera_name[0] == camera_name[2] && camera_config[0] == camera_config[2]) {
ui->camera_mode->setCurrentIndex(0); // Single
}
}
updateCameraMode();
setConfiguration();
});
connect(ui->camera_mode, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged),
this, [this] {
stopPreviewing();
ui->camera_position_label->setVisible(ui->camera_mode->currentIndex() == 1);
ui->camera_position->setVisible(ui->camera_mode->currentIndex() == 1);
current_selected = getCameraSelection();
});
connect(ui->camera_position,
static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this, [this] {
stopPreviewing();
if (getCameraSelection() != current_selected) {
recordConfig();
}
setConfiguration();
});
connect(ui->toolButton, &QToolButton::clicked, this, &ConfigureCamera::onToolButtonClicked);
connect(ui->preview_button, &QPushButton::clicked, this, [=] { startPreviewing(); });
connect(ui->prompt_before_load, &QCheckBox::stateChanged, this, [this](int state) {
ui->camera_file->setDisabled(state == Qt::Checked);
ui->toolButton->setDisabled(state == Qt::Checked);
if (state == Qt::Checked) {
ui->camera_file->setText("");
}
});
connect(ui->camera_file, &QLineEdit::textChanged, this, [=] { stopPreviewing(); });
connect(ui->system_camera,
static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this,
[=] { stopPreviewing(); });
}
void ConfigureCamera::updateCameraMode() {
CameraPosition pos = getCameraSelection();
// Set the visibility of the camera mode selection widgets
if (pos == CameraPosition::RearBoth) {
ui->camera_position->setHidden(true);
ui->camera_position_label->setHidden(true);
ui->camera_mode->setHidden(false);
ui->camera_mode_label->setHidden(false);
} else {
ui->camera_position->setHidden(pos == CameraPosition::Front);
ui->camera_position_label->setHidden(pos == CameraPosition::Front);
ui->camera_mode->setHidden(pos == CameraPosition::Front);
ui->camera_mode_label->setHidden(pos == CameraPosition::Front);
}
}
void ConfigureCamera::updateImageSourceUI() {
int image_source = ui->image_source->currentIndex();
switch (image_source) {
case 0: /* blank */
case 2: /* system camera */
ui->prompt_before_load->setHidden(true);
ui->prompt_before_load->setChecked(false);
ui->camera_file_label->setHidden(true);
ui->camera_file->setHidden(true);
ui->camera_file->setText("");
ui->toolButton->setHidden(true);
break;
case 1: /* still image */
ui->prompt_before_load->setHidden(false);
ui->camera_file_label->setHidden(false);
ui->camera_file->setHidden(false);
ui->toolButton->setHidden(false);
if (camera_config[getSelectedCameraIndex()].empty()) {
ui->prompt_before_load->setChecked(true);
ui->camera_file->setDisabled(true);
ui->toolButton->setDisabled(true);
ui->camera_file->setText("");
} else {
ui->camera_file->setDisabled(false);
ui->toolButton->setDisabled(false);
}
break;
default:
NGLOG_ERROR(Service_CAM, "Unknown image source {}", image_source);
}
ui->system_camera_label->setHidden(image_source != 2);
ui->system_camera->setHidden(image_source != 2);
}
void ConfigureCamera::recordConfig() {
std::string implementation = Implementations[ui->image_source->currentIndex()];
int image_source = ui->image_source->currentIndex();
std::string config;
if (image_source == 2) { /* system camera */
if (ui->system_camera->currentIndex() == 0) {
config = "";
} else {
config = ui->system_camera->currentText().toStdString();
}
} else {
config = ui->camera_file->text().toStdString();
}
if (current_selected == CameraPosition::RearBoth) {
camera_name[0] = camera_name[2] = implementation;
camera_config[0] = camera_config[2] = config;
} else if (current_selected != CameraPosition::Null) {
int index = static_cast<int>(current_selected);
camera_name[index] = implementation;
camera_config[index] = config;
}
current_selected = getCameraSelection();
}
void ConfigureCamera::startPreviewing() {
current_selected = getCameraSelection();
recordConfig();
int camera_selection = getSelectedCameraIndex();
stopPreviewing();
// Init preview box
ui->preview_box->setHidden(false);
ui->preview_button->setHidden(true);
preview_width = ui->preview_box->size().width();
preview_height = preview_width * 0.75;
ui->preview_box->setToolTip(tr("Resolution: ") + QString::number(preview_width) + "*" +
QString::number(preview_height));
// Load previewing camera
previewing_camera =
Camera::CreateCameraPreview(camera_name[camera_selection], camera_config[camera_selection],
preview_width, preview_height);
if (!previewing_camera) {
stopPreviewing();
return;
}
previewing_camera->SetResolution(
{static_cast<u16>(preview_width), static_cast<u16>(preview_height)});
previewing_camera->SetEffect(Service::CAM::Effect::None);
previewing_camera->SetFlip(Service::CAM::Flip::None);
previewing_camera->SetFormat(Service::CAM::OutputFormat::RGB565);
previewing_camera->SetFrameRate(Service::CAM::FrameRate::Rate_30);
previewing_camera->StartCapture();
timer_id = startTimer(1000 / 30);
}
void ConfigureCamera::stopPreviewing() {
ui->preview_box->setHidden(true);
ui->preview_button->setHidden(false);
if (previewing_camera) {
previewing_camera->StopCapture();
}
if (timer_id != 0) {
killTimer(timer_id);
timer_id = 0;
}
}
void ConfigureCamera::timerEvent(QTimerEvent* event) {
if (event->timerId() != timer_id) {
return;
}
if (!previewing_camera) {
killTimer(timer_id);
timer_id = 0;
return;
}
std::vector<u16> frame = previewing_camera->ReceiveFrame();
int width = ui->preview_box->size().width();
int height = width * 0.75;
if (width != preview_width || height != preview_height) {
stopPreviewing();
return;
}
QImage image(width, height, QImage::Format::Format_RGB16);
std::memcpy(image.bits(), frame.data(), width * height * sizeof(u16));
ui->preview_box->setPixmap(QPixmap::fromImage(image));
}
void ConfigureCamera::setConfiguration() {
int index = getSelectedCameraIndex();
for (int i = 0; i < Implementations.size(); i++) {
if (Implementations[i] == camera_name[index]) {
ui->image_source->setCurrentIndex(i);
}
}
if (camera_name[index] == "image") {
ui->camera_file->setDisabled(camera_config[index].empty());
ui->toolButton->setDisabled(camera_config[index].empty());
if (camera_config[index].empty()) {
ui->camera_file->setText("");
}
}
if (camera_name[index] == "qt") {
ui->system_camera->setCurrentIndex(0);
if (!camera_config[index].empty()) {
ui->system_camera->setCurrentText(QString::fromStdString(camera_config[index]));
}
} else {
ui->camera_file->setText(QString::fromStdString(camera_config[index]));
}
updateImageSourceUI();
}
void ConfigureCamera::onToolButtonClicked() {
stopPreviewing();
int camera_selection = getSelectedCameraIndex();
QString filter;
if (camera_name[camera_selection] == "image") {
QList<QByteArray> types = QImageReader::supportedImageFormats();
QList<QString> temp_filters;
for (const QByteArray& type : types) {
temp_filters << QString("*." + QString(type));
}
filter = tr("Supported image files (%1)").arg(temp_filters.join(" "));
}
QString path = QFileDialog::getOpenFileName(this, tr("Open File"), ".", filter);
if (!path.isEmpty()) {
ui->camera_file->setText(path);
}
}
void ConfigureCamera::applyConfiguration() {
recordConfig();
stopPreviewing();
Settings::values.camera_name = camera_name;
Settings::values.camera_config = camera_config;
Settings::Apply();
}
ConfigureCamera::CameraPosition ConfigureCamera::getCameraSelection() {
switch (ui->camera_selection->currentIndex()) {
case 0: // Front
return CameraPosition::Front;
case 1: // Rear
if (ui->camera_mode->currentIndex() == 0) {
// Single (2D) mode
return CameraPosition::RearBoth;
} else {
// Double (3D) mode
return (ui->camera_position->currentIndex() == 0) ? CameraPosition::RearLeft
: CameraPosition::RearRight;
}
default:
NGLOG_ERROR(Frontend, "Unknown camera selection");
return CameraPosition::Front;
}
}
int ConfigureCamera::getSelectedCameraIndex() {
CameraPosition pos = getCameraSelection();
int camera_selection = static_cast<int>(pos);
if (pos == CameraPosition::RearBoth) { // Single Mode
camera_selection = 0; // Either camera is the same, so we return RearRight
}
return camera_selection;
}
void ConfigureCamera::retranslateUi() {
ui->retranslateUi(this);
}