citra_qt/dumping: Add dumping dialog
This is the main dialog of video dumping. This dialog allows the user to set output format, output path, video/audio encoder and video/audio bitrate. When a format is selected, the list of video and audio encoders are updated. Only encoders of codecs that can be contained in the format is shown.
This commit is contained in:
parent
e769d90aa8
commit
f82ba41fe0
3 changed files with 434 additions and 0 deletions
210
src/citra_qt/dumping/dumping_dialog.cpp
Normal file
210
src/citra_qt/dumping/dumping_dialog.cpp
Normal file
|
@ -0,0 +1,210 @@
|
|||
// Copyright 2020 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <QFileDialog>
|
||||
#include <QMessageBox>
|
||||
#include "citra_qt/dumping/dumping_dialog.h"
|
||||
#include "citra_qt/dumping/options_dialog.h"
|
||||
#include "citra_qt/uisettings.h"
|
||||
#include "core/settings.h"
|
||||
#include "ui_dumping_dialog.h"
|
||||
|
||||
DumpingDialog::DumpingDialog(QWidget* parent)
|
||||
: QDialog(parent), ui(std::make_unique<Ui::DumpingDialog>()) {
|
||||
|
||||
ui->setupUi(this);
|
||||
|
||||
connect(ui->pathExplore, &QToolButton::clicked, this, &DumpingDialog::OnToolButtonClicked);
|
||||
connect(ui->buttonBox, &QDialogButtonBox::accepted, [this] {
|
||||
ApplyConfiguration();
|
||||
accept();
|
||||
});
|
||||
connect(ui->buttonBox, &QDialogButtonBox::rejected, this, &DumpingDialog::reject);
|
||||
connect(ui->formatOptionsButton, &QPushButton::clicked, [this] {
|
||||
OpenOptionsDialog(formats.at(ui->formatComboBox->currentData().toUInt()).options,
|
||||
format_options);
|
||||
});
|
||||
connect(ui->videoEncoderOptionsButton, &QPushButton::clicked, [this] {
|
||||
OpenOptionsDialog(
|
||||
video_encoders.at(ui->videoEncoderComboBox->currentData().toUInt()).options,
|
||||
video_encoder_options);
|
||||
});
|
||||
connect(ui->audioEncoderOptionsButton, &QPushButton::clicked, [this] {
|
||||
OpenOptionsDialog(
|
||||
audio_encoders.at(ui->audioEncoderComboBox->currentData().toUInt()).options,
|
||||
audio_encoder_options);
|
||||
});
|
||||
|
||||
SetConfiguration();
|
||||
|
||||
connect(ui->formatComboBox, qOverload<int>(&QComboBox::currentIndexChanged), [this] {
|
||||
ui->pathLineEdit->setText(QString{});
|
||||
format_options.clear();
|
||||
PopulateEncoders();
|
||||
});
|
||||
|
||||
connect(ui->videoEncoderComboBox, qOverload<int>(&QComboBox::currentIndexChanged),
|
||||
[this] { video_encoder_options.clear(); });
|
||||
connect(ui->audioEncoderComboBox, qOverload<int>(&QComboBox::currentIndexChanged),
|
||||
[this] { audio_encoder_options.clear(); });
|
||||
}
|
||||
|
||||
DumpingDialog::~DumpingDialog() = default;
|
||||
|
||||
QString DumpingDialog::GetFilePath() const {
|
||||
return ui->pathLineEdit->text();
|
||||
}
|
||||
|
||||
void DumpingDialog::Populate() {
|
||||
formats = VideoDumper::ListFormats();
|
||||
video_encoders = VideoDumper::ListEncoders(AVMEDIA_TYPE_VIDEO);
|
||||
audio_encoders = VideoDumper::ListEncoders(AVMEDIA_TYPE_AUDIO);
|
||||
|
||||
// Check that these are not empty
|
||||
QString missing;
|
||||
if (formats.empty()) {
|
||||
missing = tr("output formats");
|
||||
}
|
||||
if (video_encoders.empty()) {
|
||||
missing = tr("video encoders");
|
||||
}
|
||||
if (audio_encoders.empty()) {
|
||||
missing = tr("audio encoders");
|
||||
}
|
||||
|
||||
if (!missing.isEmpty()) {
|
||||
QMessageBox::critical(this, tr("Citra"),
|
||||
tr("Could not find any available %1.\nPlease check your FFmpeg "
|
||||
"installation used for compilation.")
|
||||
.arg(missing));
|
||||
reject();
|
||||
return;
|
||||
}
|
||||
|
||||
// Populate formats
|
||||
for (std::size_t i = 0; i < formats.size(); ++i) {
|
||||
const auto& format = formats[i];
|
||||
|
||||
// Check format: only formats that have video encoders and audio encoders are displayed
|
||||
bool has_video = false;
|
||||
for (const auto& video_encoder : video_encoders) {
|
||||
if (format.supported_video_codecs.count(video_encoder.codec)) {
|
||||
has_video = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!has_video)
|
||||
continue;
|
||||
|
||||
bool has_audio = false;
|
||||
for (const auto& audio_encoder : audio_encoders) {
|
||||
if (format.supported_audio_codecs.count(audio_encoder.codec)) {
|
||||
has_audio = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!has_audio)
|
||||
continue;
|
||||
|
||||
ui->formatComboBox->addItem(tr("%1 (%2)").arg(QString::fromStdString(format.long_name),
|
||||
QString::fromStdString(format.name)),
|
||||
static_cast<unsigned long long>(i));
|
||||
if (format.name == Settings::values.output_format) {
|
||||
ui->formatComboBox->setCurrentIndex(ui->formatComboBox->count() - 1);
|
||||
}
|
||||
}
|
||||
PopulateEncoders();
|
||||
}
|
||||
|
||||
void DumpingDialog::PopulateEncoders() {
|
||||
const auto& format = formats.at(ui->formatComboBox->currentData().toUInt());
|
||||
|
||||
ui->videoEncoderComboBox->clear();
|
||||
for (std::size_t i = 0; i < video_encoders.size(); ++i) {
|
||||
const auto& video_encoder = video_encoders[i];
|
||||
if (!format.supported_video_codecs.count(video_encoder.codec)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
ui->videoEncoderComboBox->addItem(
|
||||
tr("%1 (%2)").arg(QString::fromStdString(video_encoder.long_name),
|
||||
QString::fromStdString(video_encoder.name)),
|
||||
static_cast<unsigned long long>(i));
|
||||
if (video_encoder.name == Settings::values.video_encoder) {
|
||||
ui->videoEncoderComboBox->setCurrentIndex(ui->videoEncoderComboBox->count() - 1);
|
||||
}
|
||||
}
|
||||
|
||||
ui->audioEncoderComboBox->clear();
|
||||
for (std::size_t i = 0; i < audio_encoders.size(); ++i) {
|
||||
const auto& audio_encoder = audio_encoders[i];
|
||||
if (!format.supported_audio_codecs.count(audio_encoder.codec)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
ui->audioEncoderComboBox->addItem(
|
||||
tr("%1 (%2)").arg(QString::fromStdString(audio_encoder.long_name),
|
||||
QString::fromStdString(audio_encoder.name)),
|
||||
static_cast<unsigned long long>(i));
|
||||
if (audio_encoder.name == Settings::values.audio_encoder) {
|
||||
ui->audioEncoderComboBox->setCurrentIndex(ui->audioEncoderComboBox->count() - 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DumpingDialog::OnToolButtonClicked() {
|
||||
const auto& format = formats.at(ui->formatComboBox->currentData().toUInt());
|
||||
|
||||
QString extensions;
|
||||
for (const auto& ext : format.extensions) {
|
||||
if (!extensions.isEmpty()) {
|
||||
extensions.append(QLatin1Char{' '});
|
||||
}
|
||||
extensions.append(QStringLiteral("*.%1").arg(QString::fromStdString(ext)));
|
||||
}
|
||||
|
||||
const auto path = QFileDialog::getSaveFileName(
|
||||
this, tr("Select Video Output Path"), last_path,
|
||||
tr("%1 (%2)").arg(QString::fromStdString(format.long_name), extensions));
|
||||
if (!path.isEmpty()) {
|
||||
last_path = QFileInfo(ui->pathLineEdit->text()).path();
|
||||
ui->pathLineEdit->setText(path);
|
||||
}
|
||||
}
|
||||
|
||||
void DumpingDialog::OpenOptionsDialog(const std::vector<VideoDumper::OptionInfo>& options,
|
||||
std::string& current_value) {
|
||||
OptionsDialog dialog(this, options, current_value);
|
||||
if (dialog.exec() != QDialog::DialogCode::Accepted) {
|
||||
return;
|
||||
}
|
||||
|
||||
current_value = dialog.GetCurrentValue();
|
||||
}
|
||||
|
||||
void DumpingDialog::SetConfiguration() {
|
||||
Populate();
|
||||
|
||||
format_options = Settings::values.format_options;
|
||||
video_encoder_options = Settings::values.video_encoder_options;
|
||||
audio_encoder_options = Settings::values.audio_encoder_options;
|
||||
last_path = UISettings::values.video_dumping_path;
|
||||
ui->videoBitrateSpinBox->setValue(static_cast<int>(Settings::values.video_bitrate));
|
||||
ui->audioBitrateSpinBox->setValue(static_cast<int>(Settings::values.audio_bitrate));
|
||||
}
|
||||
|
||||
void DumpingDialog::ApplyConfiguration() {
|
||||
Settings::values.output_format = formats.at(ui->formatComboBox->currentData().toUInt()).name;
|
||||
Settings::values.format_options = format_options;
|
||||
Settings::values.video_encoder =
|
||||
video_encoders.at(ui->videoEncoderComboBox->currentData().toUInt()).name;
|
||||
Settings::values.video_encoder_options = video_encoder_options;
|
||||
Settings::values.video_bitrate = ui->videoBitrateSpinBox->value();
|
||||
Settings::values.audio_encoder =
|
||||
audio_encoders.at(ui->audioEncoderComboBox->currentData().toUInt()).name;
|
||||
Settings::values.audio_encoder_options = audio_encoder_options;
|
||||
Settings::values.audio_bitrate = ui->audioBitrateSpinBox->value();
|
||||
UISettings::values.video_dumping_path = last_path;
|
||||
Settings::Apply();
|
||||
}
|
41
src/citra_qt/dumping/dumping_dialog.h
Normal file
41
src/citra_qt/dumping/dumping_dialog.h
Normal file
|
@ -0,0 +1,41 @@
|
|||
// Copyright 2020 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <memory>
|
||||
#include <QDialog>
|
||||
#include "core/dumping/ffmpeg_backend.h"
|
||||
|
||||
namespace Ui {
|
||||
class DumpingDialog;
|
||||
}
|
||||
|
||||
class DumpingDialog : public QDialog {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit DumpingDialog(QWidget* parent);
|
||||
~DumpingDialog() override;
|
||||
|
||||
QString GetFilePath() const;
|
||||
void ApplyConfiguration();
|
||||
|
||||
private:
|
||||
void Populate();
|
||||
void PopulateEncoders();
|
||||
void SetConfiguration();
|
||||
void OnToolButtonClicked();
|
||||
void OpenOptionsDialog(const std::vector<VideoDumper::OptionInfo>& options,
|
||||
std::string& current_value);
|
||||
|
||||
std::unique_ptr<Ui::DumpingDialog> ui;
|
||||
std::string format_options;
|
||||
std::string video_encoder_options;
|
||||
std::string audio_encoder_options;
|
||||
|
||||
QString last_path;
|
||||
|
||||
std::vector<VideoDumper::FormatInfo> formats;
|
||||
std::vector<VideoDumper::EncoderInfo> video_encoders;
|
||||
std::vector<VideoDumper::EncoderInfo> audio_encoders;
|
||||
};
|
183
src/citra_qt/dumping/dumping_dialog.ui
Normal file
183
src/citra_qt/dumping/dumping_dialog.ui
Normal file
|
@ -0,0 +1,183 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>DumpingDialog</class>
|
||||
<widget class="QDialog" name="DumpingDialog">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>600</width>
|
||||
<height>360</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Dump Video</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout">
|
||||
<item>
|
||||
<widget class="QGroupBox">
|
||||
<property name="title">
|
||||
<string>Output</string>
|
||||
</property>
|
||||
<layout class="QGridLayout">
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel">
|
||||
<property name="text">
|
||||
<string>Format:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QComboBox" name="formatComboBox"/>
|
||||
</item>
|
||||
<item row="0" column="2">
|
||||
<widget class="QPushButton" name="formatOptionsButton">
|
||||
<property name="text">
|
||||
<string>Options...</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel">
|
||||
<property name="text">
|
||||
<string>Path:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QLineEdit" name="pathLineEdit"/>
|
||||
</item>
|
||||
<item row="1" column="2">
|
||||
<widget class="QToolButton" name="pathExplore">
|
||||
<property name="text">
|
||||
<string>...</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox">
|
||||
<property name="title">
|
||||
<string>Video</string>
|
||||
</property>
|
||||
<layout class="QGridLayout">
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel">
|
||||
<property name="text">
|
||||
<string>Encoder:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QComboBox" name="videoEncoderComboBox">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="2">
|
||||
<widget class="QPushButton" name="videoEncoderOptionsButton">
|
||||
<property name="text">
|
||||
<string>Options...</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel">
|
||||
<property name="text">
|
||||
<string>Bitrate:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QSpinBox" name="videoBitrateSpinBox">
|
||||
<property name="maximum">
|
||||
<number>10000000</number>
|
||||
</property>
|
||||
<property name="singleStep">
|
||||
<number>1000</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="2">
|
||||
<widget class="QLabel">
|
||||
<property name="text">
|
||||
<string>bps</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox">
|
||||
<property name="title">
|
||||
<string>Audio</string>
|
||||
</property>
|
||||
<layout class="QGridLayout">
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel">
|
||||
<property name="text">
|
||||
<string>Encoder:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QComboBox" name="audioEncoderComboBox">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="2">
|
||||
<widget class="QPushButton" name="audioEncoderOptionsButton">
|
||||
<property name="text">
|
||||
<string>Options...</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel">
|
||||
<property name="text">
|
||||
<string>Bitrate:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QSpinBox" name="audioBitrateSpinBox">
|
||||
<property name="maximum">
|
||||
<number>1000000</number>
|
||||
</property>
|
||||
<property name="singleStep">
|
||||
<number>100</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="2">
|
||||
<widget class="QLabel">
|
||||
<property name="text">
|
||||
<string>bps</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QDialogButtonBox" name="buttonBox">
|
||||
<property name="standardButtons">
|
||||
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</ui>
|
Loading…
Reference in a new issue